def test_site_name_default(self): site = Site( hostname="example.com", port=80, site_name="example dot com", is_default_site=True, ) self.assertEqual(site.__str__(), "example dot com [default]")
def test_oops_there_is_more_than_one(self): Site.objects.create( hostname="example.com", is_default_site=True, root_page=Page.objects.get(pk=2), ) site = Site( hostname="test.com", is_default_site=True, root_page=Page.objects.get(pk=2) ) with self.assertRaises(Site.MultipleObjectsReturned): # If there already are multiple default sites, you're in trouble site.clean_fields()
def test_for_request_result_caching(self): # repeat test to show caching is unique per request instance, # even when the requests are for the same site for i, request in enumerate([self.get_request(), self.get_request()], 1): with self.subTest(attempt=i): # force site query beforehand Site.find_for_request(request) # only the first lookup should result in a query with self.assertNumQueries(1): for i in range(4): TestSiteSetting.for_request(request)
def test_models_cached(self): """Accessing a setting should only hit the DB once per render""" get_title = '{{ settings("tests.testgenericsetting").title }}' request = self.get_request() # run extra query before hand Site.find_for_request(request) for i in range(1, 4): with self.assertNumQueries(1): context = {"request": request} template = self.engine.from_string(get_title * i) self.assertEqual(template.render(context), self.default_settings.title * i)
def test_select_related(self, expected_queries=4): """The `select_related` attribute on setting models is `None` by default, so fetching foreign keys values requires additional queries""" request = self.get_request() self._create_importantpages_object() # force site query beforehand Site.find_for_request(request) # fetch settings and access foreiegn keys with self.assertNumQueries(expected_queries): settings = ImportantPages.for_request(request) settings.sign_up_page settings.general_terms_page settings.privacy_policy_page
def resolve_pages(self, info, **kwargs): pages = (WagtailPage.objects.live().public().filter( depth__gt=1).specific()) # no need to the root page if kwargs.get("in_site", False): site = Site.find_for_request(info.context) pages = pages.in_site(site) content_type = kwargs.pop("content_type", None) if content_type: app_label, model = content_type.strip().lower().split(".") try: ctype = ContentType.objects.get(app_label=app_label, model=model) except: # noqa return (WagtailPage.objects.none() ) # something not quite right here, bail out early else: pages = pages.filter(content_type=ctype) language_code = kwargs.pop("language_code", None) if language_code: pages = pages.filter(locale__language_code=language_code) return resolve_queryset(pages, info, **kwargs)
def slugurl(context, slug): """ Returns the URL for the page that has the given slug. First tries to find a page on the current site. If that fails or a request is not available in the context, then returns the URL for the first page that matches the slug on any site. """ page = None try: site = Site.find_for_request(context["request"]) current_site = site except KeyError: # No site object found - allow the fallback below to take place. pass else: if current_site is not None: page = Page.objects.in_site(current_site).filter(slug=slug).first() # If no page is found, fall back to searching the whole tree. if page is None: page = Page.objects.filter(slug=slug).first() if page: # call pageurl() instead of page.relative_url() here so we get the ``accepts_kwarg`` logic return pageurl(context, page)
def get_wagtail_site(self): from wagtail.models import Site site = Site.find_for_request(self.request) if site is None: return Site.objects.select_related("root_page").get(is_default_site=True) return site
def redirect_to_relevant_instance(request, app_name, model_name): model = get_model_from_url_params(app_name, model_name) if issubclass(model, BaseSiteSetting): # Redirect the user to the edit page for the current site # (or the current request does not correspond to a site, the first site in the list) site_request = Site.find_for_request(request) site = site_request or Site.objects.first() if not site: messages.error( request, _("This setting could not be opened because there is no site defined."), ) return redirect("wagtailadmin_home") return redirect( "wagtailsettings:edit", app_name, model_name, site.pk, ) elif issubclass(model, BaseGenericSetting): return redirect( "wagtailsettings:edit", app_name, model_name, model.load(request_or_site=request).id, ) else: raise NotImplementedError
def test_models_cached(self): """Accessing a setting should only hit the DB once per request instance, even if using that request to rendering multiple times""" request = self.get_request() get_title = "{{ settings.tests.testgenericsetting.title }}" # force site query beforehand Site.find_for_request(request) with self.assertNumQueries(1): for i in range(1, 4): with self.subTest(attempt=i): self.assertEqual( self.render(request, get_title * i), self.default_settings.title * i, )
def pageurl(context, page, fallback=None): """ Outputs a page's URL as relative (/foo/bar/) if it's within the same site as the current page, or absolute (http://example.com/foo/bar/) if not. If kwargs contains a fallback view name and page is None, the fallback view url will be returned. """ if page is None and fallback: return resolve_url(fallback) if not hasattr(page, "relative_url"): raise ValueError("pageurl tag expected a Page object, got %r" % page) try: site = Site.find_for_request(context["request"]) current_site = site except KeyError: # request not available in the current context; fall back on page.url return page.url if current_site is None: # request does not correspond to a recognised site; fall back on page.url return page.url # Pass page.relative_url the request object, which may contain a cached copy of # Site.get_site_root_paths() # This avoids page.relative_url having to make a database/cache fetch for this list # each time it's called. return page.relative_url(current_site, request=context.get("request"))
def test_get_base_url_prefers_setting(self): request = RequestFactory().get("/") site = self.prepare_site() self.assertEqual(site, Site.find_for_request(request)) self.assertEqual(get_base_url(request), "https://bar.example.com") with override_settings(WAGTAILAPI_BASE_URL=None): self.assertEqual(get_base_url(request), "http://other.example.com:8080")
def _inner(request): site = Site.find_for_request(request) if site is None: # find_for_request() can't determine the site, # so no settings can be idenfified return {} else: return SettingsProxy(request)
def test_get_urls_with_request_site_cache_with_i18n(self): request, django_site = self.get_request_and_django_site("/sitemap.xml") req_protocol = request.scheme sitemap = Sitemap(request) # pre-seed find_for_request cache, so that it's not counted towards the query count Site.find_for_request(request) with self.assertNumQueries(16): urls = [ url["location"] for url in sitemap.get_urls(1, django_site, req_protocol) ] self.assertIn("http://localhost/", urls) # Homepage self.assertIn("http://localhost/hello-world/", urls) # Child page
def process_request(self, request): """ Set request.site to contain the Site object responsible for handling this request, according to hostname matching rules """ try: request.site = Site.find_for_request(request) except Site.DoesNotExist: request.site = None
def test_result_order_when_multiple_sites_share_the_same_root_page(self): result = Site.get_site_root_paths() # An entry for the default site should come first self.assertEqual(result[0][0], self.default_site.id) # Followed by entries for others in 'host' alphabetical order self.assertEqual(result[1][0], self.abc_site.id) self.assertEqual(result[2][0], self.def_site.id)
def get_base_url(request=None): site = Site.find_for_request(request) base_url = getattr(settings, 'WAGTAILAPI_BASE_URL', site.root_url if request and site else None) if base_url: # We only want the scheme and netloc base_url_parsed = urlparse(base_url) return base_url_parsed.scheme + '://' + base_url_parsed.netloc
def resolve_page(self, info, **kwargs): return preview_observable( id=kwargs.get("id"), slug=kwargs.get("slug"), url_path=kwargs.get("url_path"), token=kwargs.get("token"), content_type=kwargs.get("content_type"), site=Site.find_for_request(info.context) if kwargs.get( "in_site", False) else None, )
def wagtail_site(context): """ Returns the Site object for the given request """ try: request = context["request"] except KeyError: return None return Site.find_for_request(request=request)
def resolve_page(self, info, **kwargs): return get_specific_page( id=kwargs.get("id"), slug=kwargs.get("slug"), url_path=kwargs.get("url_path"), language_code=kwargs.get("language_code"), token=kwargs.get("token"), content_type=kwargs.get("content_type"), site=Site.find_for_request(info.context) if kwargs.get( "in_site", False) else None, )
def edit_current_site(request, app_name, model_name): # Redirect the user to the edit page for the current site # (or the current request does not correspond to a site, the first site in the list) site_request = Site.find_for_request(request) site = site_request or Site.objects.first() if not site: messages.error( request, _("This setting could not be opened because there is no site defined." ), ) return redirect("wagtailadmin_home") return redirect("wagtailsettings:edit", app_name, model_name, site.pk)
def _get_redirect(request, path): if ("\0" in path ): # reject URLs with null characters, which crash on Postgres (#4496) return None site = Site.find_for_request(request) try: return models.Redirect.get_for_site(site).get(old_path=path) except models.Redirect.MultipleObjectsReturned: # We have a site-specific and a site-ambivalent redirect; prefer the specific one return models.Redirect.objects.get(site=site, old_path=path) except models.Redirect.DoesNotExist: return None
def get_base_url(request=None): base_url = getattr(settings, "WAGTAILAPI_BASE_URL", None) if base_url is None and request: site = Site.find_for_request(request) if site: base_url = site.root_url if base_url: # We only want the scheme and netloc base_url_parsed = urlparse(force_str(base_url)) return base_url_parsed.scheme + "://" + base_url_parsed.netloc
def for_request(cls, request): """ Get or create an instance of this model for the request, and cache the result on the request for faster repeat access. """ attr_name = cls.get_cache_attr_name() if hasattr(request, attr_name): return getattr(request, attr_name) site = Site.find_for_request(request) site_settings = cls.for_site(site) # to allow more efficient page url generation site_settings._request = request setattr(request, attr_name, site_settings) return site_settings
def get_setting(context, model_string, use_default_site=False): cache_key = None if use_default_site: cache_key = Site.objects.get(is_default_site=True) elif "request" in context: cache_key = Site.find_for_request(context["request"]) # Sadly, WeakKeyDictionary can not implement __missing__, so we have to do # this one manually try: context_cache = settings_cache[context] except KeyError: context_cache = settings_cache[context] = SettingContextCache() # These ones all implement __missing__ in a useful way though return context_cache[cache_key][model_string]
def find_object(self, queryset, request): site = Site.find_for_request(request) if 'html_path' in request.GET and site is not None: path = request.GET['html_path'] path_components = [component for component in path.split('/') if component] try: page, _, _ = site.root_page.specific.route(request, path_components) except Http404: return if queryset.filter(id=page.id).exists(): return page return super().find_object(queryset, request)
def test_get_base_url_from_request(self): # base url for siteless request should be None request = RequestFactory().get("/") self.assertIsNone(Site.find_for_request(request)) self.assertIsNone(get_base_url(request)) # base url for request with a site should be based on the site's details site = self.prepare_site() self.clear_cached_site(request) self.assertEqual(site, Site.find_for_request(request)) self.assertEqual(get_base_url(request), "http://other.example.com:8080") # port 443 should indicate https without a port site.port = 443 site.save() self.clear_cached_site(request) self.assertEqual(get_base_url(request), "https://other.example.com") # port 80 should indicate http without a port site.port = 80 site.save() self.clear_cached_site(request) self.assertEqual(get_base_url(request), "http://other.example.com")
def serve(request, path): # we need a valid Site object corresponding to this request in order to proceed site = Site.find_for_request(request) if not site: raise Http404 path_components = [component for component in path.split("/") if component] page, args, kwargs = site.root_page.localized.specific.route( request, path_components ) for fn in hooks.get_hooks("before_serve_page"): result = fn(page, request, args, kwargs) if isinstance(result, HttpResponse): return result return page.serve(request, *args, **kwargs)
def get_setting(context, model_string, use_default_site=False): if use_default_site: site = Site.objects.get(is_default_site=True) elif "request" in context: site = Site.find_for_request(context["request"]) else: raise RuntimeError("No request found in context, and use_default_site " "flag not set") # Sadly, WeakKeyDictionary can not implement __missing__, so we have to do # this one manually try: context_cache = settings_cache[context] except KeyError: context_cache = settings_cache[context] = ContextCache() # These ones all implement __missing__ in a useful way though return context_cache[site][model_string]
def get_base_queryset(self): """ Returns a queryset containing all pages that can be seen by this user. This is used as the base for get_queryset and is also used to find the parent pages when using the child_of and descendant_of filters as well. """ # Get live pages that are not in a private section queryset = Page.objects.all().public().live() site = Site.find_for_request(self.request) # Filter by site if site: queryset = queryset.descendant_of(site.root_page, inclusive=True) else: # No sites configured queryset = queryset.none() return queryset