Fixed #18620 -- Made ContentTypes shortcut view prefer current site if available.

Thanks Mike Tigas (mtigas) for the initial patch.
This commit is contained in:
Paulo 2017-09-20 11:26:03 -05:00 committed by Tim Graham
parent efbcd60a22
commit d14850e525
2 changed files with 41 additions and 29 deletions

View File

@ -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:

View File

@ -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):