89 lines
3.5 KiB
Python
89 lines
3.5 KiB
Python
|
from django.apps import apps
|
|||
|
from django.contrib.contenttypes.models import ContentType
|
|||
|
from django.contrib.sites.shortcuts import get_current_site
|
|||
|
from django.core.exceptions import ObjectDoesNotExist
|
|||
|
from django.http import Http404, HttpResponseRedirect
|
|||
|
from django.utils.translation import gettext as _
|
|||
|
|
|||
|
|
|||
|
def shortcut(request, content_type_id, object_id):
|
|||
|
"""
|
|||
|
Redirect to an object's page based on a content-type ID and an object ID.
|
|||
|
"""
|
|||
|
# Look up the object, making sure it's got a get_absolute_url() function.
|
|||
|
try:
|
|||
|
content_type = ContentType.objects.get(pk=content_type_id)
|
|||
|
if not content_type.model_class():
|
|||
|
raise Http404(
|
|||
|
_("Content type %(ct_id)s object has no associated model")
|
|||
|
% {"ct_id": content_type_id}
|
|||
|
)
|
|||
|
obj = content_type.get_object_for_this_type(pk=object_id)
|
|||
|
except (ObjectDoesNotExist, ValueError):
|
|||
|
raise Http404(
|
|||
|
_("Content type %(ct_id)s object %(obj_id)s doesn’t exist")
|
|||
|
% {"ct_id": content_type_id, "obj_id": object_id}
|
|||
|
)
|
|||
|
|
|||
|
try:
|
|||
|
get_absolute_url = obj.get_absolute_url
|
|||
|
except AttributeError:
|
|||
|
raise Http404(
|
|||
|
_("%(ct_name)s objects don’t have a get_absolute_url() method")
|
|||
|
% {"ct_name": content_type.name}
|
|||
|
)
|
|||
|
absurl = get_absolute_url()
|
|||
|
|
|||
|
# Try to figure out the object's domain, so we can do a cross-site redirect
|
|||
|
# if necessary.
|
|||
|
|
|||
|
# If the object actually defines a domain, we're done.
|
|||
|
if absurl.startswith(("http://", "https://", "//")):
|
|||
|
return HttpResponseRedirect(absurl)
|
|||
|
|
|||
|
# Otherwise, we need to introspect the object's relationships for a
|
|||
|
# relation to the Site object
|
|||
|
try:
|
|||
|
object_domain = get_current_site(request).domain
|
|||
|
except ObjectDoesNotExist:
|
|||
|
object_domain = None
|
|||
|
|
|||
|
if apps.is_installed("django.contrib.sites"):
|
|||
|
Site = apps.get_model("sites.Site")
|
|||
|
opts = obj._meta
|
|||
|
|
|||
|
for field in opts.many_to_many:
|
|||
|
# Look for a many-to-many relationship to Site.
|
|||
|
if field.remote_field.model is Site:
|
|||
|
site_qs = getattr(obj, field.name).all()
|
|||
|
if object_domain and site_qs.filter(domain=object_domain).exists():
|
|||
|
# The current site's domain matches a site attached to the
|
|||
|
# object.
|
|||
|
break
|
|||
|
# Caveat: In the case of multiple related Sites, this just
|
|||
|
# selects the *first* one, which is arbitrary.
|
|||
|
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:
|
|||
|
if field.remote_field and field.remote_field.model is Site:
|
|||
|
try:
|
|||
|
site = getattr(obj, field.name)
|
|||
|
except Site.DoesNotExist:
|
|||
|
continue
|
|||
|
if site is not None:
|
|||
|
object_domain = site.domain
|
|||
|
break
|
|||
|
|
|||
|
# If all that malarkey found an object domain, use it. Otherwise, fall back
|
|||
|
# to whatever get_absolute_url() returned.
|
|||
|
if object_domain is not None:
|
|||
|
protocol = request.scheme
|
|||
|
return HttpResponseRedirect("%s://%s%s" % (protocol, object_domain, absurl))
|
|||
|
else:
|
|||
|
return HttpResponseRedirect(absurl)
|