learning-website-django1-1 Exercise 3: django.contrib.sites or a Config File? ============================================================================== Django has a built-in sites framework. Decide between it and the plain config file by trying both: look up the current site from a request host with contrib.sites (database) and with the site map (no database), and see what each does with a host it does not know. Save as sites_demo.py in the project folder and run it: """sites_demo.py: django.contrib.sites against a plain config lookup (run from the project folder).""" import django from django.conf import settings from config.sites_config import SITES, site_hosts HOSTS = site_hosts("prod") settings.configure( INSTALLED_APPS=["django.contrib.sites"], DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}}, ALLOWED_HOSTS=HOSTS + ["unknown.example.org"], DEFAULT_AUTO_FIELD="django.db.models.BigAutoField", USE_TZ=True, ) django.setup() from django.contrib.sites.models import Site from django.contrib.sites.shortcuts import get_current_site from django.core.management import call_command from django.db import connection from django.test import RequestFactory from django.test.utils import CaptureQueriesContext, override_settings rf = RequestFactory() print("1. A fresh database after migrate():") call_command("migrate", verbosity=0) print(" sites in the database:", [(s.id, s.domain) for s in Site.objects.all()]) with override_settings(SITE_ID=1): print(" Site.objects.get_current() with SITE_ID=1 ->", Site.objects.get_current().domain) print("\n2. Add our eight sites (this data now lives in the DATABASE, not in the code):") for host in HOSTS: Site.objects.get_or_create(domain=host, defaults={"name": host}) print(" rows:", Site.objects.count()) print("\n3. With SITE_ID unset, the site is found from the request host:") Site.objects.clear_cache() for host in ("languages.osztromok.com", "unknown.example.org"): request = rf.get("/", HTTP_HOST=host) try: with CaptureQueriesContext(connection) as q: site = get_current_site(request) print(f" {host:<26} -> {site.domain} ({len(q)} query)") except Site.DoesNotExist: print(f" {host:<26} -> Site.DoesNotExist (an unknown host breaks the request)") print("\n4. The same lookup from the config file, no database at all:") BY_HOST = {h: name for name, h in zip(SITES, HOSTS)} with CaptureQueriesContext(connection) as q: print(" languages.osztromok.com ->", BY_HOST.get("languages.osztromok.com")) print(" unknown.example.org ->", BY_HOST.get("unknown.example.org"), "(None: we choose what happens)") print(" database queries used:", len(q)) python sites_demo.py Output (checked by running it): 1. A fresh database after migrate(): sites in the database: [(1, 'example.com')] Site.objects.get_current() with SITE_ID=1 -> example.com 2. Add our eight sites (this data now lives in the DATABASE, not in the code): rows: 9 3. With SITE_ID unset, the site is found from the request host: languages.osztromok.com -> languages.osztromok.com (1 query) unknown.example.org -> Site.DoesNotExist (an unknown host breaks the request) 4. The same lookup from the config file, no database at all: languages.osztromok.com -> languages unknown.example.org -> None (None: we choose what happens) database queries used: 0 What it shows ------------- 1. A fresh database already contains a site called example.com. You must replace or add to it, and every environment (your PC, the server, tests) needs the same rows. 2. The eight sites would have to be copied into the database, so the list of sites now lives in TWO places (the map in the code, and the table). They can disagree. 3. With SITE_ID unset, the site is found from the request host, with one query (cached afterwards). An unknown host raises Site.DoesNotExist, which is a server error unless you catch it. 4. The config lookup needs no database, and an unknown host simply gives None, so you decide the response (a 404, or a redirect to the landing page). Decision for this course ------------------------ Use the config file. The content comes from files, not a database, the site list already exists in one place, and 0 queries beats 1. Reasons you might choose django.contrib.sites later: you add a database model with a ForeignKey to Site (per-site content in the database), or you use a Django feature built on it (such as the flatpages or redirects apps). Nothing in this layout prevents adding it then. WHY THIS WORKS AS AN ANSWER --------------------------- Reading the documentation tells you what a feature does. Running it shows what it costs: the default row, the duplicated list and the exception on an unknown host were all visible in a dozen lines of output, and they are what settle the choice.