1from django.apps import apps
2from django.db.models import Case, IntegerField, Q, When
3
4
5MATCH_HOSTNAME_PORT = 0
6MATCH_HOSTNAME_DEFAULT = 1
7MATCH_DEFAULT = 2
8MATCH_HOSTNAME = 3
9
10
11def get_site_for_hostname(hostname, port):
12    """Return the wagtailcore.Site object for the given hostname and port."""
13    Site = apps.get_model('wagtailcore.Site')
14
15    sites = list(Site.objects.annotate(match=Case(
16        # annotate the results by best choice descending
17
18        # put exact hostname+port match first
19        When(hostname=hostname, port=port, then=MATCH_HOSTNAME_PORT),
20
21        # then put hostname+default (better than just hostname or just default)
22        When(hostname=hostname, is_default_site=True, then=MATCH_HOSTNAME_DEFAULT),
23
24        # then match default with different hostname. there is only ever
25        # one default, so order it above (possibly multiple) hostname
26        # matches so we can use sites[0] below to access it
27        When(is_default_site=True, then=MATCH_DEFAULT),
28
29        # because of the filter below, if it's not default then its a hostname match
30        default=MATCH_HOSTNAME,
31
32        output_field=IntegerField(),
33    )).filter(Q(hostname=hostname) | Q(is_default_site=True)).order_by(
34        'match'
35    ).select_related(
36        'root_page'
37    ))
38
39    if sites:
40        # if theres a unique match or hostname (with port or default) match
41        if len(sites) == 1 or sites[0].match in (MATCH_HOSTNAME_PORT, MATCH_HOSTNAME_DEFAULT):
42            return sites[0]
43
44        # if there is a default match with a different hostname, see if
45        # there are many hostname matches. if only 1 then use that instead
46        # otherwise we use the default
47        if sites[0].match == MATCH_DEFAULT:
48            return sites[len(sites) == 2]
49
50    raise Site.DoesNotExist()
51