mirror of https://github.com/django/django.git
Fixed #18620 -- Made ContentTypes shortcut view prefer current site if available.
Thanks Mike Tigas (mtigas) for the initial patch.
This commit is contained in:
parent
efbcd60a22
commit
d14850e525
|
@ -1,6 +1,6 @@
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.sites.requests import RequestSite
|
from django.contrib.sites.shortcuts import get_current_site
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.http import Http404, HttpResponseRedirect
|
from django.http import Http404, HttpResponseRedirect
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
@ -43,27 +43,32 @@ def shortcut(request, content_type_id, object_id):
|
||||||
|
|
||||||
# Otherwise, we need to introspect the object's relationships for a
|
# Otherwise, we need to introspect the object's relationships for a
|
||||||
# relation to the Site object
|
# relation to the Site object
|
||||||
object_domain = None
|
try:
|
||||||
|
object_domain = get_current_site(request).domain
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
object_domain = None
|
||||||
|
|
||||||
if apps.is_installed('django.contrib.sites'):
|
if apps.is_installed('django.contrib.sites'):
|
||||||
Site = apps.get_model('sites.Site')
|
Site = apps.get_model('sites.Site')
|
||||||
|
|
||||||
opts = obj._meta
|
opts = obj._meta
|
||||||
|
|
||||||
# First, look for a many-to-many relationship to Site.
|
|
||||||
for field in opts.many_to_many:
|
for field in opts.many_to_many:
|
||||||
|
# Look for a many-to-many relationship to Site.
|
||||||
if field.remote_field.model is Site:
|
if field.remote_field.model is Site:
|
||||||
try:
|
site_qs = getattr(obj, field.name).all()
|
||||||
# Caveat: In the case of multiple related Sites, this just
|
if object_domain and site_qs.filter(domain=object_domain).exists():
|
||||||
# selects the *first* one, which is arbitrary.
|
# The current site's domain matches a site attached to the
|
||||||
object_domain = getattr(obj, field.name).all()[0].domain
|
# object.
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
if object_domain is not None:
|
|
||||||
break
|
break
|
||||||
|
# Caveat: In the case of multiple related Sites, this just
|
||||||
# Next, look for a many-to-one relationship to Site.
|
# selects the *first* one, which is arbitrary.
|
||||||
if object_domain is None:
|
site = site_qs.first()
|
||||||
|
if site:
|
||||||
|
object_domain = site.domain
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# No many-to-many relationship to Site found. Look for a
|
||||||
|
# many-to-one relationship to Site.
|
||||||
for field in obj._meta.fields:
|
for field in obj._meta.fields:
|
||||||
if field.remote_field and field.remote_field.model is Site:
|
if field.remote_field and field.remote_field.model is Site:
|
||||||
try:
|
try:
|
||||||
|
@ -72,20 +77,8 @@ def shortcut(request, content_type_id, object_id):
|
||||||
continue
|
continue
|
||||||
if site is not None:
|
if site is not None:
|
||||||
object_domain = site.domain
|
object_domain = site.domain
|
||||||
if object_domain is not None:
|
|
||||||
break
|
break
|
||||||
|
|
||||||
# Fall back to the current site (if possible).
|
|
||||||
if object_domain is None:
|
|
||||||
try:
|
|
||||||
object_domain = Site.objects.get_current(request).domain
|
|
||||||
except Site.DoesNotExist:
|
|
||||||
pass
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Fall back to the current request's site.
|
|
||||||
object_domain = RequestSite(request).domain
|
|
||||||
|
|
||||||
# If all that malarkey found an object domain, use it. Otherwise, fall back
|
# If all that malarkey found an object domain, use it. Otherwise, fall back
|
||||||
# to whatever get_absolute_url() returned.
|
# to whatever get_absolute_url() returned.
|
||||||
if object_domain is not None:
|
if object_domain is not None:
|
||||||
|
|
|
@ -106,13 +106,15 @@ class ContentTypesViewsTests(TestCase):
|
||||||
obj = ModelWithNullFKToSite.objects.create(title='title')
|
obj = ModelWithNullFKToSite.objects.create(title='title')
|
||||||
url = '/shortcut/%s/%s/' % (ContentType.objects.get_for_model(ModelWithNullFKToSite).id, obj.pk)
|
url = '/shortcut/%s/%s/' % (ContentType.objects.get_for_model(ModelWithNullFKToSite).id, obj.pk)
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertRedirects(response, '%s' % obj.get_absolute_url(), fetch_redirect_response=False)
|
expected_url = 'http://testserver%s' % obj.get_absolute_url()
|
||||||
|
self.assertRedirects(response, expected_url, fetch_redirect_response=False)
|
||||||
|
|
||||||
@mock.patch('django.apps.apps.get_model')
|
@mock.patch('django.apps.apps.get_model')
|
||||||
def test_shortcut_view_with_site_m2m(self, get_model):
|
def test_shortcut_view_with_site_m2m(self, get_model):
|
||||||
"""
|
"""
|
||||||
When the object has a ManyToManyField to Site, redirect to the
|
When the object has a ManyToManyField to Site, redirect to the current
|
||||||
domain of the first site found in the m2m relationship.
|
site if it's attached to the object or to the domain of the first site
|
||||||
|
found in the m2m relationship.
|
||||||
"""
|
"""
|
||||||
get_model.side_effect = lambda *args, **kwargs: MockSite if args[0] == 'sites.Site' else ModelWithM2MToSite
|
get_model.side_effect = lambda *args, **kwargs: MockSite if args[0] == 'sites.Site' else ModelWithM2MToSite
|
||||||
|
|
||||||
|
@ -138,6 +140,23 @@ class ContentTypesViewsTests(TestCase):
|
||||||
response = self.client.get('/shortcut/%s/%s/' % (ct.pk, site_3_obj.pk))
|
response = self.client.get('/shortcut/%s/%s/' % (ct.pk, site_3_obj.pk))
|
||||||
self.assertRedirects(response, expected_url, fetch_redirect_response=False)
|
self.assertRedirects(response, expected_url, fetch_redirect_response=False)
|
||||||
|
|
||||||
|
obj_with_sites = ModelWithM2MToSite.objects.create(title='Linked to Current Site')
|
||||||
|
obj_with_sites.sites.set(MockSite.objects.all())
|
||||||
|
shortcut_url = '/shortcut/%s/%s/' % (ct.pk, obj_with_sites.pk)
|
||||||
|
expected_url = 'http://example2.com%s' % obj_with_sites.get_absolute_url()
|
||||||
|
|
||||||
|
with self.settings(SITE_ID=2):
|
||||||
|
# Redirects to the domain of the Site matching the current site's
|
||||||
|
# domain.
|
||||||
|
response = self.client.get(shortcut_url)
|
||||||
|
self.assertRedirects(response, expected_url, fetch_redirect_response=False)
|
||||||
|
|
||||||
|
with self.settings(SITE_ID=None, ALLOWED_HOSTS=['example2.com']):
|
||||||
|
# Redirects to the domain of the Site matching the request's host
|
||||||
|
# header.
|
||||||
|
response = self.client.get(shortcut_url, SERVER_NAME='example2.com')
|
||||||
|
self.assertRedirects(response, expected_url, fetch_redirect_response=False)
|
||||||
|
|
||||||
|
|
||||||
class ShortcutViewTests(TestCase):
|
class ShortcutViewTests(TestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue