django-multisite2 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_multisite2-2.0.0.dist-info/LICENSE +28 -0
- django_multisite2-2.0.0.dist-info/METADATA +246 -0
- django_multisite2-2.0.0.dist-info/RECORD +51 -0
- django_multisite2-2.0.0.dist-info/WHEEL +5 -0
- django_multisite2-2.0.0.dist-info/top_level.txt +2 -0
- multisite/__init__.py +1 -0
- multisite/admin/__init__.py +3 -0
- multisite/admin/alias_admin.py +58 -0
- multisite/admin/multisite_changelist.py +64 -0
- multisite/admin/multisite_model_admin.py +161 -0
- multisite/apps.py +29 -0
- multisite/exceptions.py +57 -0
- multisite/forms.py +22 -0
- multisite/hacks.py +163 -0
- multisite/management/__init__.py +0 -0
- multisite/management/commands/__init__.py +0 -0
- multisite/management/commands/update_public_suffix_list.py +33 -0
- multisite/middleware/__init__.py +4 -0
- multisite/middleware/cookie_domain_middleware.py +72 -0
- multisite/middleware/dynamic_site_middleware.py +198 -0
- multisite/migrations/0001_initial.py +71 -0
- multisite/migrations/0002_alter_alias_id_alter_alias_is_canonical.py +32 -0
- multisite/migrations/0003_alter_alias_options_alter_alias_unique_together_and_more.py +41 -0
- multisite/migrations/0004_alter_alias_managers.py +22 -0
- multisite/migrations/__init__.py +0 -0
- multisite/models/__init__.py +3 -0
- multisite/models/alias.py +110 -0
- multisite/models/managers.py +196 -0
- multisite/models/signals.py +36 -0
- multisite/models/validators.py +14 -0
- multisite/tests/__init__.py +0 -0
- multisite/tests/get_test_allowed_hosts.py +14 -0
- multisite/tests/get_test_http_response.py +13 -0
- multisite/tests/hosts.py +46 -0
- multisite/tests/test_settings.py +23 -0
- multisite/tests/tests/__init__.py +0 -0
- multisite/tests/tests/request_factory.py +12 -0
- multisite/tests/tests/test_alias.py +271 -0
- multisite/tests/tests/test_contrib_site.py +21 -0
- multisite/tests/tests/test_cookie_domain_middleware.py +196 -0
- multisite/tests/tests/test_dynamic_site_middleware.py +262 -0
- multisite/tests/tests/test_site_cache.py +127 -0
- multisite/tests/tests/test_site_domain.py +24 -0
- multisite/tests/tests/test_site_id.py +76 -0
- multisite/tests/tests/test_template_loader.py +49 -0
- multisite/threadlocals.py +136 -0
- multisite/utils.py +172 -0
- multisite_app/__init__.py +0 -0
- multisite_app/apps.py +6 -0
- multisite_app/urls.py +5 -0
- multisite_app/views.py +8 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.contrib.sites.models import Site
|
|
3
|
+
from django.core.exceptions import ImproperlyConfigured
|
|
4
|
+
from django.http import Http404, HttpResponse
|
|
5
|
+
from django.test import TestCase, override_settings, tag
|
|
6
|
+
|
|
7
|
+
from multisite import SiteID
|
|
8
|
+
from multisite.exceptions import MultisiteCacheError, MultisiteError
|
|
9
|
+
from multisite.middleware import DynamicSiteMiddleware
|
|
10
|
+
from multisite.models import Alias
|
|
11
|
+
|
|
12
|
+
from ..get_test_allowed_hosts import get_test_allowed_hosts
|
|
13
|
+
from ..get_test_http_response import get_test_http_response
|
|
14
|
+
from .request_factory import RequestFactory
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@override_settings(
|
|
18
|
+
ROOT_URLCONF="multisite_app.urls",
|
|
19
|
+
SITE_ID=SiteID(default=0),
|
|
20
|
+
CACHE_MULTISITE_ALIAS="multisite",
|
|
21
|
+
CACHES={
|
|
22
|
+
"default": {"BACKEND": "django.core.cache.backends.dummy.DummyCache"},
|
|
23
|
+
"multisite": {"BACKEND": "django.core.cache.backends.dummy.DummyCache"},
|
|
24
|
+
},
|
|
25
|
+
MULTISITE_FALLBACK=None,
|
|
26
|
+
ALLOWED_HOSTS=get_test_allowed_hosts("example.com", "anothersite.example", replace=True),
|
|
27
|
+
)
|
|
28
|
+
class DynamicSiteMiddlewareTest(TestCase):
|
|
29
|
+
def setUp(self):
|
|
30
|
+
self.host = "example.com"
|
|
31
|
+
self.factory = RequestFactory(host=self.host)
|
|
32
|
+
|
|
33
|
+
Site.objects.all().delete()
|
|
34
|
+
self.site = Site.objects.create(pk=1, domain=self.host)
|
|
35
|
+
self.site2 = Site.objects.create(pk=2, domain="anothersite.example")
|
|
36
|
+
|
|
37
|
+
def test_valid_domain(self):
|
|
38
|
+
# Make the request
|
|
39
|
+
request = self.factory.get("/")
|
|
40
|
+
self.assertEqual(DynamicSiteMiddleware(HttpResponse)(request).status_code, 200)
|
|
41
|
+
self.assertEqual(settings.SITE_ID, self.site.pk)
|
|
42
|
+
# Request again
|
|
43
|
+
self.assertEqual(DynamicSiteMiddleware(HttpResponse)(request).status_code, 200)
|
|
44
|
+
self.assertEqual(settings.SITE_ID, self.site.pk)
|
|
45
|
+
|
|
46
|
+
def test_valid_domain_port(self):
|
|
47
|
+
# Make the request with a specific port
|
|
48
|
+
request = self.factory.get("/", host=self.host + ":8000")
|
|
49
|
+
self.assertEqual(DynamicSiteMiddleware(HttpResponse)(request).status_code, 200)
|
|
50
|
+
self.assertEqual(settings.SITE_ID, self.site.pk)
|
|
51
|
+
# Request again
|
|
52
|
+
self.assertEqual(DynamicSiteMiddleware(HttpResponse)(request).status_code, 200)
|
|
53
|
+
self.assertEqual(settings.SITE_ID, self.site.pk)
|
|
54
|
+
|
|
55
|
+
def test_case_sensitivity(self):
|
|
56
|
+
# Make the request in all uppercase
|
|
57
|
+
request = self.factory.get("/", host=self.host.upper())
|
|
58
|
+
self.assertEqual(DynamicSiteMiddleware(HttpResponse)(request).status_code, 200)
|
|
59
|
+
self.assertEqual(settings.SITE_ID, self.site.pk)
|
|
60
|
+
|
|
61
|
+
def test_change_domain(self):
|
|
62
|
+
# Make the request
|
|
63
|
+
request = self.factory.get("/")
|
|
64
|
+
self.assertEqual(DynamicSiteMiddleware(HttpResponse)(request).status_code, 200)
|
|
65
|
+
self.assertEqual(settings.SITE_ID, self.site.pk)
|
|
66
|
+
# Another request with a different site
|
|
67
|
+
request = self.factory.get("/", host=self.site2.domain)
|
|
68
|
+
self.assertEqual(DynamicSiteMiddleware(HttpResponse)(request).status_code, 200)
|
|
69
|
+
self.assertEqual(settings.SITE_ID, self.site2.pk)
|
|
70
|
+
|
|
71
|
+
def test_unknown_host(self):
|
|
72
|
+
# Unknown host
|
|
73
|
+
request = self.factory.get("/", host="unknown")
|
|
74
|
+
with self.assertRaises(Http404):
|
|
75
|
+
DynamicSiteMiddleware(HttpResponse)(request)
|
|
76
|
+
# The middleware resets SiteID to its default value, as given above, on error.
|
|
77
|
+
self.assertEqual(settings.SITE_ID, 0)
|
|
78
|
+
|
|
79
|
+
def test_unknown_hostport(self):
|
|
80
|
+
# Unknown host:port
|
|
81
|
+
request = self.factory.get("/", host="unknown:8000")
|
|
82
|
+
with self.assertRaises(Http404):
|
|
83
|
+
DynamicSiteMiddleware(HttpResponse)(request)
|
|
84
|
+
# The middleware resets SiteID to its default value, as given above, on error.
|
|
85
|
+
self.assertEqual(settings.SITE_ID, 0)
|
|
86
|
+
|
|
87
|
+
def test_invalid_host(self):
|
|
88
|
+
# Invalid host
|
|
89
|
+
request = self.factory.get("/", host="")
|
|
90
|
+
with self.assertRaises(Http404):
|
|
91
|
+
DynamicSiteMiddleware(HttpResponse)(request)
|
|
92
|
+
|
|
93
|
+
def test_invalid_hostport(self):
|
|
94
|
+
# Invalid host:port
|
|
95
|
+
request = self.factory.get("/", host=":8000")
|
|
96
|
+
with self.assertRaises(Http404):
|
|
97
|
+
DynamicSiteMiddleware(HttpResponse)(request)
|
|
98
|
+
|
|
99
|
+
def test_no_sites(self):
|
|
100
|
+
# FIXME: this needs to go into its own TestCase since
|
|
101
|
+
# it requires modifying the fixture to work properly
|
|
102
|
+
# Remove all Sites
|
|
103
|
+
Site.objects.all().delete()
|
|
104
|
+
# Make the request
|
|
105
|
+
request = self.factory.get("/")
|
|
106
|
+
self.assertRaises(Http404, DynamicSiteMiddleware(HttpResponse), request)
|
|
107
|
+
# The middleware resets SiteID to its default value, as given above, on error.
|
|
108
|
+
self.assertEqual(settings.SITE_ID, 0)
|
|
109
|
+
|
|
110
|
+
@override_settings(MULTISITE_DEBUG=True)
|
|
111
|
+
def test_debug_no_sites_raises(self):
|
|
112
|
+
Site.objects.all().delete()
|
|
113
|
+
request = self.factory.get("/")
|
|
114
|
+
self.assertRaises(MultisiteCacheError, DynamicSiteMiddleware(HttpResponse), request)
|
|
115
|
+
|
|
116
|
+
@override_settings(MULTISITE_DEBUG=False)
|
|
117
|
+
def test_no_sites_raises(self):
|
|
118
|
+
Site.objects.all().delete()
|
|
119
|
+
request = self.factory.get("/")
|
|
120
|
+
self.assertRaises(Http404, DynamicSiteMiddleware(HttpResponse), request)
|
|
121
|
+
|
|
122
|
+
@override_settings(ALLOWED_HOSTS=["example.org"])
|
|
123
|
+
def test_redirect(self):
|
|
124
|
+
host = "example.org"
|
|
125
|
+
alias = Alias.objects.create(site=self.site, domain=host)
|
|
126
|
+
self.assertTrue(alias.redirect_to_canonical)
|
|
127
|
+
# Make the request
|
|
128
|
+
http_response = get_test_http_response()
|
|
129
|
+
request = self.factory.get("/path", host=host)
|
|
130
|
+
response = DynamicSiteMiddleware(http_response)(request)
|
|
131
|
+
self.assertEqual(response.status_code, 301)
|
|
132
|
+
self.assertEqual(response["Location"], "http://%s/path" % self.host)
|
|
133
|
+
|
|
134
|
+
@override_settings(ALLOWED_HOSTS=["example.org"])
|
|
135
|
+
def test_no_redirect(self):
|
|
136
|
+
host = "example.org"
|
|
137
|
+
Alias.objects.create(site=self.site, domain=host, redirect_to_canonical=False)
|
|
138
|
+
# Make the request
|
|
139
|
+
http_response = get_test_http_response()
|
|
140
|
+
request = self.factory.get("/path", host=host)
|
|
141
|
+
self.assertEqual(DynamicSiteMiddleware(http_response)(request).status_code, 200)
|
|
142
|
+
self.assertEqual(settings.SITE_ID, self.site.pk)
|
|
143
|
+
|
|
144
|
+
@tag("9")
|
|
145
|
+
def test_integration(self):
|
|
146
|
+
"""Test that the middleware loads and runs properly
|
|
147
|
+
under settings.MIDDLEWARE.
|
|
148
|
+
"""
|
|
149
|
+
self.assertTrue(Site.objects.get(domain="example.com"))
|
|
150
|
+
self.assertTrue(Site.objects.get(domain="anothersite.example"))
|
|
151
|
+
|
|
152
|
+
response = self.client.get("/domain/", HTTP_HOST=self.host)
|
|
153
|
+
self.assertEqual(response.status_code, 200)
|
|
154
|
+
self.assertContains(response, self.site.domain)
|
|
155
|
+
self.assertEqual(settings.SITE_ID, self.site.pk)
|
|
156
|
+
|
|
157
|
+
response = self.client.get("/domain/", HTTP_HOST=self.site2.domain)
|
|
158
|
+
self.assertEqual(response.status_code, 200)
|
|
159
|
+
self.assertContains(response, self.site2.domain)
|
|
160
|
+
self.assertEqual(settings.SITE_ID, self.site2.pk)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@override_settings(
|
|
164
|
+
SITE_ID=SiteID(default=0),
|
|
165
|
+
CACHE_MULTISITE_ALIAS="multisite",
|
|
166
|
+
CACHES={"multisite": {"BACKEND": "django.core.cache.backends.dummy.DummyCache"}},
|
|
167
|
+
MULTISITE_FALLBACK=None,
|
|
168
|
+
MULTISITE_FALLBACK_KWARGS={},
|
|
169
|
+
)
|
|
170
|
+
class DynamicSiteMiddlewareFallbackTest(TestCase):
|
|
171
|
+
def setUp(self):
|
|
172
|
+
self.factory = RequestFactory(host="unknown")
|
|
173
|
+
|
|
174
|
+
Site.objects.all().delete()
|
|
175
|
+
|
|
176
|
+
def test_404(self):
|
|
177
|
+
http_response = get_test_http_response()
|
|
178
|
+
request = self.factory.get("/")
|
|
179
|
+
self.assertRaises(Http404, DynamicSiteMiddleware(http_response), request)
|
|
180
|
+
self.assertEqual(settings.SITE_ID, 0)
|
|
181
|
+
|
|
182
|
+
def test_testserver(self):
|
|
183
|
+
host = "testserver"
|
|
184
|
+
site = Site.objects.create(domain=host)
|
|
185
|
+
http_response = get_test_http_response()
|
|
186
|
+
request = self.factory.get("/", host=host)
|
|
187
|
+
self.assertEqual(DynamicSiteMiddleware(http_response)(request).status_code, 200)
|
|
188
|
+
self.assertEqual(settings.SITE_ID, site.pk)
|
|
189
|
+
|
|
190
|
+
def test_string_class(self):
|
|
191
|
+
# Class based
|
|
192
|
+
settings.MULTISITE_FALLBACK = "django.views.generic.base.RedirectView"
|
|
193
|
+
settings.MULTISITE_FALLBACK_KWARGS = {
|
|
194
|
+
"url": "http://example.com/",
|
|
195
|
+
"permanent": False,
|
|
196
|
+
}
|
|
197
|
+
request = self.factory.get("/")
|
|
198
|
+
http_response = get_test_http_response()
|
|
199
|
+
response = DynamicSiteMiddleware(http_response)(request)
|
|
200
|
+
self.assertEqual(response.status_code, 302)
|
|
201
|
+
self.assertEqual(response["Location"], settings.MULTISITE_FALLBACK_KWARGS["url"])
|
|
202
|
+
|
|
203
|
+
def test_class_view(self):
|
|
204
|
+
from django.views.generic.base import RedirectView
|
|
205
|
+
|
|
206
|
+
settings.MULTISITE_FALLBACK = RedirectView.as_view(
|
|
207
|
+
url="http://example.com/", permanent=False
|
|
208
|
+
)
|
|
209
|
+
http_response = get_test_http_response()
|
|
210
|
+
request = self.factory.get("/")
|
|
211
|
+
response = DynamicSiteMiddleware(http_response)(request)
|
|
212
|
+
self.assertEqual(response.status_code, 302)
|
|
213
|
+
self.assertEqual(response["Location"], "http://example.com/")
|
|
214
|
+
|
|
215
|
+
def test_invalid(self):
|
|
216
|
+
settings.MULTISITE_FALLBACK = ""
|
|
217
|
+
http_response = get_test_http_response()
|
|
218
|
+
request = self.factory.get("/")
|
|
219
|
+
self.assertRaises(ImproperlyConfigured, DynamicSiteMiddleware(http_response), request)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@override_settings(SITE_ID=0)
|
|
223
|
+
class DynamicSiteMiddlewareSettingsTest(TestCase):
|
|
224
|
+
|
|
225
|
+
def test_invalid_settings(self):
|
|
226
|
+
http_response = get_test_http_response()
|
|
227
|
+
self.assertRaises(MultisiteError, DynamicSiteMiddleware, http_response)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
@override_settings(
|
|
231
|
+
SITE_ID=SiteID(default=0),
|
|
232
|
+
CACHE_MULTISITE_ALIAS="multisite",
|
|
233
|
+
CACHES={"multisite": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}},
|
|
234
|
+
MULTISITE_FALLBACK=None,
|
|
235
|
+
ALLOWED_HOSTS=get_test_allowed_hosts("example.com", replace=True),
|
|
236
|
+
)
|
|
237
|
+
class CacheTest(TestCase):
|
|
238
|
+
def setUp(self):
|
|
239
|
+
self.host = "example.com"
|
|
240
|
+
self.factory = RequestFactory(host=self.host)
|
|
241
|
+
|
|
242
|
+
Site.objects.all().delete()
|
|
243
|
+
self.site = Site.objects.create(domain=self.host)
|
|
244
|
+
|
|
245
|
+
def test_site_domain_changed(self):
|
|
246
|
+
# Test to ensure that the cache is cleared properly
|
|
247
|
+
http_response = get_test_http_response()
|
|
248
|
+
middleware = DynamicSiteMiddleware(http_response)
|
|
249
|
+
cache_key = middleware.get_cache_key(self.host)
|
|
250
|
+
self.assertEqual(middleware.cache.get(cache_key), None)
|
|
251
|
+
# Make the request
|
|
252
|
+
request = self.factory.get("/")
|
|
253
|
+
self.assertEqual(middleware(request).status_code, 200)
|
|
254
|
+
self.assertEqual(middleware.cache.get(cache_key).site_id, self.site.pk)
|
|
255
|
+
# Change the domain name
|
|
256
|
+
self.site.domain = "example.org"
|
|
257
|
+
self.site.save()
|
|
258
|
+
self.assertEqual(middleware.cache.get(cache_key), None)
|
|
259
|
+
# Make the request again, which will now be invalid
|
|
260
|
+
request = self.factory.get("/")
|
|
261
|
+
self.assertRaises(Http404, middleware, request)
|
|
262
|
+
self.assertEqual(settings.SITE_ID, 0)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.contrib.sites.models import Site
|
|
3
|
+
from django.test import TestCase, override_settings
|
|
4
|
+
|
|
5
|
+
from multisite import SiteID
|
|
6
|
+
from multisite.hacks import use_framework_for_site_cache
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@override_settings(SITE_ID=SiteID())
|
|
10
|
+
class SiteCacheTest(TestCase):
|
|
11
|
+
def _initialize_cache(self):
|
|
12
|
+
# initialize cache again so override key prefix settings are used
|
|
13
|
+
from django.contrib.sites import models
|
|
14
|
+
|
|
15
|
+
use_framework_for_site_cache()
|
|
16
|
+
self.cache = models.SITE_CACHE
|
|
17
|
+
|
|
18
|
+
def setUp(self):
|
|
19
|
+
from django.contrib.sites import models
|
|
20
|
+
|
|
21
|
+
if hasattr(models, "clear_site_cache"):
|
|
22
|
+
# Before Django 1.6, the Site cache is cleared after the Site
|
|
23
|
+
# object has been created. This replicates that behaviour.
|
|
24
|
+
def save(self, *args, **kwargs):
|
|
25
|
+
super(models.Site, self).save(*args, **kwargs)
|
|
26
|
+
models.SITE_CACHE.clear()
|
|
27
|
+
|
|
28
|
+
models.Site.save = save
|
|
29
|
+
|
|
30
|
+
self._initialize_cache()
|
|
31
|
+
Site.objects.all().delete()
|
|
32
|
+
self.host = "example.com"
|
|
33
|
+
self.site = Site.objects.create(domain=self.host)
|
|
34
|
+
settings.SITE_ID.set(self.site.id)
|
|
35
|
+
|
|
36
|
+
def test_get_current(self):
|
|
37
|
+
self.assertRaises(KeyError, self.cache.__getitem__, self.site.id)
|
|
38
|
+
# Populate cache
|
|
39
|
+
self.assertEqual(Site.objects.get_current(), self.site)
|
|
40
|
+
self.assertEqual(self.cache[self.site.id], self.site)
|
|
41
|
+
self.assertEqual(self.cache.get(key=self.site.id), self.site)
|
|
42
|
+
self.assertEqual(self.cache.get(key=-1), None) # Site doesn't exist
|
|
43
|
+
self.assertEqual(self.cache.get(-1, "Default"), "Default") # Site doesn't exist
|
|
44
|
+
self.assertEqual(
|
|
45
|
+
self.cache.get(key=-1, default="Non-existant"), "Non-existant"
|
|
46
|
+
) # Site doesn't exist
|
|
47
|
+
self.assertEqual(
|
|
48
|
+
"Non-existant",
|
|
49
|
+
self.cache.get(self.site.id, default="Non-existant", version=100),
|
|
50
|
+
) # Wrong key version 3
|
|
51
|
+
# Clear cache
|
|
52
|
+
self.cache.clear()
|
|
53
|
+
self.assertRaises(KeyError, self.cache.__getitem__, self.site.id)
|
|
54
|
+
self.assertEqual(self.cache.get(key=self.site.id, default="Cleared"), "Cleared")
|
|
55
|
+
|
|
56
|
+
def test_create_site(self):
|
|
57
|
+
self.assertEqual(Site.objects.get_current(), self.site)
|
|
58
|
+
self.assertEqual(Site.objects.get_current().domain, self.site.domain)
|
|
59
|
+
# Create new site
|
|
60
|
+
site = Site.objects.create(domain="example.org")
|
|
61
|
+
settings.SITE_ID.set(site.id)
|
|
62
|
+
self.assertEqual(Site.objects.get_current(), site)
|
|
63
|
+
self.assertEqual(Site.objects.get_current().domain, site.domain)
|
|
64
|
+
|
|
65
|
+
def test_change_site(self):
|
|
66
|
+
self.assertEqual(Site.objects.get_current(), self.site)
|
|
67
|
+
self.assertEqual(Site.objects.get_current().domain, self.site.domain)
|
|
68
|
+
# Change site domain
|
|
69
|
+
self.site.domain = "example.org"
|
|
70
|
+
self.site.save()
|
|
71
|
+
self.assertEqual(Site.objects.get_current(), self.site)
|
|
72
|
+
self.assertEqual(Site.objects.get_current().domain, self.site.domain)
|
|
73
|
+
|
|
74
|
+
def test_delete_site(self):
|
|
75
|
+
self.assertEqual(Site.objects.get_current(), self.site)
|
|
76
|
+
self.assertEqual(Site.objects.get_current().domain, self.site.domain)
|
|
77
|
+
# Delete site
|
|
78
|
+
self.site.delete()
|
|
79
|
+
self.assertRaises(KeyError, self.cache.__getitem__, self.site.id)
|
|
80
|
+
|
|
81
|
+
@override_settings(CACHE_MULTISITE_KEY_PREFIX="__test__")
|
|
82
|
+
def test_multisite_key_prefix(self):
|
|
83
|
+
self._initialize_cache()
|
|
84
|
+
# Populate cache
|
|
85
|
+
self.assertEqual(Site.objects.get_current(), self.site)
|
|
86
|
+
self.assertEqual(self.cache[self.site.id], self.site)
|
|
87
|
+
self.assertEqual(
|
|
88
|
+
self.cache._cache._get_cache_key(self.site.id),
|
|
89
|
+
"sites.{}.{}".format(settings.CACHE_MULTISITE_KEY_PREFIX, self.site.id),
|
|
90
|
+
self.cache._cache._get_cache_key(self.site.id),
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
@override_settings(
|
|
94
|
+
CACHE_MULTISITE_ALIAS="multisite",
|
|
95
|
+
CACHES={
|
|
96
|
+
"multisite": {
|
|
97
|
+
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
|
98
|
+
"KEY_PREFIX": "looselycoupled",
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
)
|
|
102
|
+
def test_default_key_prefix(self):
|
|
103
|
+
"""
|
|
104
|
+
If CACHE_MULTISITE_KEY_PREFIX is undefined,
|
|
105
|
+
the caching system should use CACHES[current]['KEY_PREFIX'].
|
|
106
|
+
"""
|
|
107
|
+
self._initialize_cache()
|
|
108
|
+
# Populate cache
|
|
109
|
+
self.assertEqual(Site.objects.get_current(), self.site)
|
|
110
|
+
self.assertEqual(self.cache[self.site.id], self.site)
|
|
111
|
+
self.assertEqual(
|
|
112
|
+
self.cache._cache._get_cache_key(self.site.id),
|
|
113
|
+
"sites.looselycoupled.{}".format(self.site.id),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
@override_settings(
|
|
117
|
+
CACHE_MULTISITE_KEY_PREFIX="virtuouslyvirtual",
|
|
118
|
+
)
|
|
119
|
+
def test_multisite_key_prefix_takes_priority_over_default(self):
|
|
120
|
+
self._initialize_cache()
|
|
121
|
+
# Populate cache
|
|
122
|
+
self.assertEqual(Site.objects.get_current(), self.site)
|
|
123
|
+
self.assertEqual(self.cache[self.site.id], self.site)
|
|
124
|
+
self.assertEqual(
|
|
125
|
+
self.cache._cache._get_cache_key(self.site.id),
|
|
126
|
+
"sites.virtuouslyvirtual.{}".format(self.site.id),
|
|
127
|
+
)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from django.contrib.sites.models import Site
|
|
2
|
+
from django.core.exceptions import ObjectDoesNotExist
|
|
3
|
+
from django.test import TestCase
|
|
4
|
+
|
|
5
|
+
from multisite import SiteDomain
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestSiteDomain(TestCase):
|
|
9
|
+
def setUp(self):
|
|
10
|
+
Site.objects.all().delete()
|
|
11
|
+
self.domain = "example.com"
|
|
12
|
+
self.site = Site.objects.create(domain=self.domain)
|
|
13
|
+
|
|
14
|
+
def test_init(self):
|
|
15
|
+
self.assertEqual(int(SiteDomain(default=self.domain)), self.site.id)
|
|
16
|
+
self.assertRaises(ObjectDoesNotExist, int, SiteDomain(default="invalid"))
|
|
17
|
+
self.assertRaises(TypeError, SiteDomain, default=None)
|
|
18
|
+
self.assertRaises(TypeError, SiteDomain, default=1)
|
|
19
|
+
|
|
20
|
+
def test_deferred_site(self):
|
|
21
|
+
domain = "example.org"
|
|
22
|
+
self.assertRaises(ObjectDoesNotExist, int, SiteDomain(default=domain))
|
|
23
|
+
site = Site.objects.create(domain=domain)
|
|
24
|
+
self.assertEqual(int(SiteDomain(default=domain)), site.id)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from django.contrib.sites.models import Site
|
|
2
|
+
from django.test import TestCase
|
|
3
|
+
|
|
4
|
+
from multisite import SiteID
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestSiteID(TestCase):
|
|
8
|
+
def setUp(self):
|
|
9
|
+
Site.objects.all().delete()
|
|
10
|
+
self.site = Site.objects.create(domain="example.com")
|
|
11
|
+
self.site_id = SiteID()
|
|
12
|
+
|
|
13
|
+
def test_invalid_default(self):
|
|
14
|
+
self.assertRaises(ValueError, SiteID, default="a")
|
|
15
|
+
self.assertRaises(ValueError, SiteID, default=self.site_id)
|
|
16
|
+
|
|
17
|
+
def test_compare_default_site_id(self):
|
|
18
|
+
self.site_id = SiteID(default=self.site.id)
|
|
19
|
+
self.assertEqual(self.site_id, self.site.id)
|
|
20
|
+
self.assertFalse(self.site_id != self.site.id)
|
|
21
|
+
self.assertFalse(self.site_id < self.site.id)
|
|
22
|
+
self.assertTrue(self.site_id <= self.site.id)
|
|
23
|
+
self.assertFalse(self.site_id > self.site.id)
|
|
24
|
+
self.assertTrue(self.site_id >= self.site.id)
|
|
25
|
+
|
|
26
|
+
def test_compare_site_ids(self):
|
|
27
|
+
self.site_id.set(1)
|
|
28
|
+
self.assertEqual(self.site_id, self.site_id)
|
|
29
|
+
self.assertFalse(self.site_id != self.site_id)
|
|
30
|
+
self.assertFalse(self.site_id < self.site_id)
|
|
31
|
+
self.assertTrue(self.site_id <= self.site_id)
|
|
32
|
+
self.assertFalse(self.site_id > self.site_id)
|
|
33
|
+
self.assertTrue(self.site_id >= self.site_id)
|
|
34
|
+
|
|
35
|
+
def test_compare_differing_types(self):
|
|
36
|
+
self.site_id.set(1)
|
|
37
|
+
self.assertNotEqual(self.site_id, "1")
|
|
38
|
+
self.assertFalse(self.site_id == "1")
|
|
39
|
+
self.assertTrue(self.site_id < "1")
|
|
40
|
+
self.assertTrue(self.site_id <= "1")
|
|
41
|
+
self.assertFalse(self.site_id > "1")
|
|
42
|
+
self.assertFalse(self.site_id >= "1")
|
|
43
|
+
self.assertNotEqual("1", self.site_id)
|
|
44
|
+
self.assertFalse("1" == self.site_id)
|
|
45
|
+
self.assertFalse("1" < self.site_id)
|
|
46
|
+
self.assertFalse("1" <= self.site_id)
|
|
47
|
+
self.assertTrue("1" > self.site_id)
|
|
48
|
+
self.assertTrue("1" >= self.site_id)
|
|
49
|
+
|
|
50
|
+
def test_set(self):
|
|
51
|
+
self.site_id.set(10)
|
|
52
|
+
self.assertEqual(int(self.site_id), 10)
|
|
53
|
+
self.site_id.set(20)
|
|
54
|
+
self.assertEqual(int(self.site_id), 20)
|
|
55
|
+
self.site_id.set(self.site)
|
|
56
|
+
self.assertEqual(int(self.site_id), self.site.id)
|
|
57
|
+
|
|
58
|
+
def test_hash(self):
|
|
59
|
+
self.site_id.set(10)
|
|
60
|
+
self.assertEqual(hash(self.site_id), 10)
|
|
61
|
+
self.site_id.set(20)
|
|
62
|
+
self.assertEqual(hash(self.site_id), 20)
|
|
63
|
+
|
|
64
|
+
def test_str_repr(self):
|
|
65
|
+
self.site_id.set(10)
|
|
66
|
+
self.assertEqual(str(self.site_id), "10")
|
|
67
|
+
self.assertEqual(repr(self.site_id), "10")
|
|
68
|
+
|
|
69
|
+
def test_context_manager(self):
|
|
70
|
+
self.assertEqual(self.site_id.site_id, None)
|
|
71
|
+
with self.site_id.override(1):
|
|
72
|
+
self.assertEqual(self.site_id.site_id, 1)
|
|
73
|
+
with self.site_id.override(2):
|
|
74
|
+
self.assertEqual(self.site_id.site_id, 2)
|
|
75
|
+
self.assertEqual(self.site_id.site_id, 1)
|
|
76
|
+
self.assertEqual(self.site_id.site_id, None)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
from io import StringIO
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from unittest import mock
|
|
7
|
+
|
|
8
|
+
from django.core.management import call_command
|
|
9
|
+
from django.template.loader import get_template
|
|
10
|
+
from django.test import TestCase, override_settings, tag
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class UpdatePublicSuffixListCommandTestCase(TestCase):
|
|
14
|
+
def setUp(self):
|
|
15
|
+
self.cache_dir = tempfile.gettempdir()
|
|
16
|
+
# save the tldextract logger output to a buffer to test output
|
|
17
|
+
self.out = StringIO()
|
|
18
|
+
self.logger = logging.getLogger("tldextract")
|
|
19
|
+
self.logger.setLevel(logging.DEBUG)
|
|
20
|
+
stdout_handler = logging.StreamHandler(self.out)
|
|
21
|
+
stdout_handler.setLevel(logging.DEBUG)
|
|
22
|
+
self.logger.addHandler(stdout_handler)
|
|
23
|
+
|
|
24
|
+
# patch tldextract to avoid actual requests
|
|
25
|
+
self.patcher = mock.patch("tldextract.TLDExtract")
|
|
26
|
+
self.tldextract = self.patcher.start()
|
|
27
|
+
|
|
28
|
+
def tearDown(self):
|
|
29
|
+
self.patcher.stop()
|
|
30
|
+
|
|
31
|
+
def tldextract_update_side_effect(self, *args, **kwargs):
|
|
32
|
+
self.logger.debug("TLDExtract.update called")
|
|
33
|
+
|
|
34
|
+
def test_command(self):
|
|
35
|
+
call_command("update_public_suffix_list")
|
|
36
|
+
expected_calls = [
|
|
37
|
+
mock.call(cache_dir=self.cache_dir),
|
|
38
|
+
mock.call().update(fetch_now=True),
|
|
39
|
+
]
|
|
40
|
+
self.assertEqual(self.tldextract.mock_calls, expected_calls)
|
|
41
|
+
|
|
42
|
+
def test_command_output(self):
|
|
43
|
+
# make sure that the logger receives output from the method
|
|
44
|
+
self.tldextract().update.side_effect = self.tldextract_update_side_effect
|
|
45
|
+
|
|
46
|
+
call_command("update_public_suffix_list", verbosity=3)
|
|
47
|
+
update_message = "Updating {}".format(self.cache_dir)
|
|
48
|
+
self.assertIn(update_message, self.out.getvalue())
|
|
49
|
+
self.assertIn("TLDExtract.update called", self.out.getvalue())
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
from contextlib import contextmanager
|
|
2
|
+
from threading import local
|
|
3
|
+
|
|
4
|
+
from django.conf import settings
|
|
5
|
+
from django.core.exceptions import ImproperlyConfigured
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SiteID(local):
|
|
9
|
+
"""
|
|
10
|
+
Dynamic settings.SITE_ID replacement, which acts like an integer.
|
|
11
|
+
|
|
12
|
+
django.contrib.sites can allow multiple Django sites to share the
|
|
13
|
+
same database. However, they cannot share the same code by
|
|
14
|
+
default.
|
|
15
|
+
|
|
16
|
+
SiteID can be used to replace the static settings.SITE_ID integer
|
|
17
|
+
when combined with the appropriate middleware.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, default=None, *args, **kwargs):
|
|
21
|
+
"""
|
|
22
|
+
``default``, if specified, determines the default SITE_ID,
|
|
23
|
+
if that is unset.
|
|
24
|
+
"""
|
|
25
|
+
if default is not None and not isinstance(default, int):
|
|
26
|
+
raise ValueError("%r is not a valid default." % default)
|
|
27
|
+
self.default = default
|
|
28
|
+
self.reset()
|
|
29
|
+
|
|
30
|
+
def __repr__(self):
|
|
31
|
+
return repr(self.__int__())
|
|
32
|
+
|
|
33
|
+
def __str__(self):
|
|
34
|
+
return str(self.__int__())
|
|
35
|
+
|
|
36
|
+
def __int__(self):
|
|
37
|
+
if self.site_id is None:
|
|
38
|
+
return self.get_default()
|
|
39
|
+
return self.site_id
|
|
40
|
+
|
|
41
|
+
def __lt__(self, other):
|
|
42
|
+
if isinstance(other, int):
|
|
43
|
+
return self.__int__() < other
|
|
44
|
+
elif isinstance(other, SiteID):
|
|
45
|
+
return self.__int__() < other.__int__()
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
def __le__(self, other):
|
|
49
|
+
if isinstance(other, int):
|
|
50
|
+
return self.__int__() <= other
|
|
51
|
+
elif isinstance(other, SiteID):
|
|
52
|
+
return self.__int__() <= other.__int__()
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
def __eq__(self, other):
|
|
56
|
+
if isinstance(other, int):
|
|
57
|
+
return self.__int__() == other
|
|
58
|
+
elif isinstance(other, SiteID):
|
|
59
|
+
return self.__int__() == other.__int__()
|
|
60
|
+
return False
|
|
61
|
+
|
|
62
|
+
def __ne__(self, other):
|
|
63
|
+
return not self.__eq__(other)
|
|
64
|
+
|
|
65
|
+
def __gt__(self, other):
|
|
66
|
+
return not self.__le__(other)
|
|
67
|
+
|
|
68
|
+
def __ge__(self, other):
|
|
69
|
+
return not self.__lt__(other)
|
|
70
|
+
|
|
71
|
+
def __hash__(self):
|
|
72
|
+
return self.__int__()
|
|
73
|
+
|
|
74
|
+
@contextmanager
|
|
75
|
+
def override(self, value):
|
|
76
|
+
"""
|
|
77
|
+
Overrides SITE_ID temporarily::
|
|
78
|
+
|
|
79
|
+
>>> with settings.SITE_ID.override(2):
|
|
80
|
+
... print settings.SITE_ID
|
|
81
|
+
2
|
|
82
|
+
"""
|
|
83
|
+
site_id_original = self.site_id
|
|
84
|
+
self.set(value)
|
|
85
|
+
try:
|
|
86
|
+
yield self
|
|
87
|
+
finally:
|
|
88
|
+
self.site_id = site_id_original
|
|
89
|
+
|
|
90
|
+
def set(self, value):
|
|
91
|
+
from django.db.models import Model
|
|
92
|
+
|
|
93
|
+
if isinstance(value, Model):
|
|
94
|
+
value = value.pk
|
|
95
|
+
self.site_id = value
|
|
96
|
+
|
|
97
|
+
def reset(self):
|
|
98
|
+
self.site_id = None
|
|
99
|
+
|
|
100
|
+
def get_default(self):
|
|
101
|
+
"""Returns the default SITE_ID."""
|
|
102
|
+
if self.default is None:
|
|
103
|
+
raise ValueError("SITE_ID has not been set.")
|
|
104
|
+
return self.default
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class SiteDomain(SiteID):
|
|
108
|
+
def __init__(self, default, *args, **kwargs):
|
|
109
|
+
"""
|
|
110
|
+
``default`` is the default domain name, resolved to SITE_ID, if
|
|
111
|
+
that is unset.
|
|
112
|
+
"""
|
|
113
|
+
# make sure they passed us a string; doing this is the single
|
|
114
|
+
# hardest py2/py3 compat headache.
|
|
115
|
+
# http://python-future.org/compatible_idioms.html#basestring and
|
|
116
|
+
# https://github.com/PythonCharmers/python-future/blob/master/src/past/types/basestring.py
|
|
117
|
+
# are not super informative, so just fall back on a literal version check:
|
|
118
|
+
if not isinstance(default, str):
|
|
119
|
+
raise TypeError("%r is not a valid default domain." % default)
|
|
120
|
+
self.default_domain = default
|
|
121
|
+
self.default = None
|
|
122
|
+
self.reset()
|
|
123
|
+
|
|
124
|
+
def get_default(self):
|
|
125
|
+
"""Returns the default SITE_ID that matches the default domain name."""
|
|
126
|
+
from django.contrib.sites.models import Site
|
|
127
|
+
|
|
128
|
+
if "django.contrib.sites" not in settings.INSTALLED_APPS:
|
|
129
|
+
raise ImproperlyConfigured(
|
|
130
|
+
"django.contrib.sites is not in settings.INSTALLED_APPS"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if self.default is None:
|
|
134
|
+
qset = Site.objects.only("id")
|
|
135
|
+
self.default = qset.get(domain=self.default_domain).id
|
|
136
|
+
return self.default
|