Browse Source

Moved RequestSite and get_current_site.

Following the app-loading refactor, these objects must live outside of
django.contrib.sites.models because they must be available without
importing the django.contrib.sites.models module when
django.contrib.sites isn't installed.

Refs #21680. Thanks Carl and Loic for reporting this issue.
Aymeric Augustin 11 years ago
parent
commit
9ffab9cee1

+ 1 - 1
django/contrib/auth/forms.py

@@ -16,7 +16,7 @@ from django.contrib.auth import authenticate, get_user_model
 from django.contrib.auth.models import User
 from django.contrib.auth.hashers import UNUSABLE_PASSWORD_PREFIX, identify_hasher
 from django.contrib.auth.tokens import default_token_generator
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites.shortcuts import get_current_site
 
 
 UNMASKED_DIGITS_TO_SHOW = 6

+ 2 - 1
django/contrib/auth/tests/test_views.py

@@ -4,7 +4,8 @@ import os
 import re
 
 from django.conf import global_settings, settings
-from django.contrib.sites.models import Site, RequestSite
+from django.contrib.sites.models import Site
+from django.contrib.sites.requests import RequestSite
 from django.contrib.admin.models import LogEntry
 from django.contrib.auth.models import User
 from django.core import mail

+ 1 - 1
django/contrib/auth/views.py

@@ -15,7 +15,7 @@ from django.contrib.auth import REDIRECT_FIELD_NAME, login as auth_login, logout
 from django.contrib.auth.decorators import login_required
 from django.contrib.auth.forms import AuthenticationForm, PasswordResetForm, SetPasswordForm, PasswordChangeForm
 from django.contrib.auth.tokens import default_token_generator
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites.shortcuts import get_current_site
 
 
 @sensitive_post_parameters()

+ 1 - 1
django/contrib/comments/feeds.py

@@ -1,5 +1,5 @@
 from django.contrib.syndication.views import Feed
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites.shortcuts import get_current_site
 from django.contrib import comments
 from django.utils.translation import ugettext as _
 

+ 1 - 1
django/contrib/comments/moderation.py

@@ -62,7 +62,7 @@ from django.contrib.comments import signals
 from django.db.models.base import ModelBase
 from django.template import Context, loader
 from django.contrib import comments
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites.shortcuts import get_current_site
 from django.utils import timezone
 
 class AlreadyModerated(Exception):

+ 1 - 1
django/contrib/contenttypes/tests.py

@@ -2,7 +2,7 @@ from __future__ import unicode_literals
 
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.views import shortcut
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites.shortcuts import get_current_site
 from django.db import models
 from django.http import HttpRequest, Http404
 from django.test import TestCase, override_settings

+ 2 - 1
django/contrib/contenttypes/views.py

@@ -2,7 +2,8 @@ from __future__ import unicode_literals
 
 from django import http
 from django.contrib.contenttypes.models import ContentType
-from django.contrib.sites.models import Site, get_current_site
+from django.contrib.sites.models import Site
+from django.contrib.sites.shortcuts import get_current_site
 from django.core.exceptions import ObjectDoesNotExist
 from django.utils.translation import ugettext as _
 

+ 1 - 1
django/contrib/flatpages/templatetags/flatpages.py

@@ -1,7 +1,7 @@
 from django import template
 from django.conf import settings
 from django.contrib.flatpages.models import FlatPage
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites.shortcuts import get_current_site
 
 
 register = template.Library()

+ 1 - 1
django/contrib/flatpages/views.py

@@ -1,6 +1,6 @@
 from django.conf import settings
 from django.contrib.flatpages.models import FlatPage
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites.shortcuts import get_current_site
 from django.http import Http404, HttpResponse, HttpResponsePermanentRedirect
 from django.shortcuts import get_object_or_404
 from django.template import loader, RequestContext

+ 1 - 1
django/contrib/gis/sitemaps/views.py

@@ -5,7 +5,7 @@ import warnings
 from django.apps import apps
 from django.http import HttpResponse, Http404
 from django.template import loader
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites.shortcuts import get_current_site
 from django.core import urlresolvers
 from django.core.paginator import EmptyPage, PageNotAnInteger
 from django.contrib.gis.db.models.fields import GeometryField

+ 1 - 1
django/contrib/redirects/middleware.py

@@ -3,7 +3,7 @@ from __future__ import unicode_literals
 from django.apps import apps
 from django.conf import settings
 from django.contrib.redirects.models import Redirect
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites.shortcuts import get_current_site
 from django.core.exceptions import ImproperlyConfigured
 from django import http
 

+ 1 - 1
django/contrib/sitemaps/views.py

@@ -1,7 +1,7 @@
 from calendar import timegm
 from functools import wraps
 
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites.shortcuts import get_current_site
 from django.core import urlresolvers
 from django.core.paginator import EmptyPage, PageNotAnInteger
 from django.http import Http404

+ 14 - 29
django/contrib/sites/models.py

@@ -1,6 +1,7 @@
 from __future__ import unicode_literals
 
 import string
+import warnings
 
 from django.core.exceptions import ImproperlyConfigured, ValidationError
 from django.db import models
@@ -8,6 +9,9 @@ from django.db.models.signals import pre_save, pre_delete
 from django.utils.translation import ugettext_lazy as _
 from django.utils.encoding import python_2_unicode_compatible
 
+from .requests import RequestSite as RealRequestSite
+from .shortcuts import get_current_site as real_get_current_site
+
 
 SITE_CACHE = {}
 
@@ -74,38 +78,19 @@ class Site(models.Model):
         return self.domain
 
 
-@python_2_unicode_compatible
-class RequestSite(object):
-    """
-    A class that shares the primary interface of Site (i.e., it has
-    ``domain`` and ``name`` attributes) but gets its data from a Django
-    HttpRequest object rather than from a database.
-
-    The save() and delete() methods raise NotImplementedError.
-    """
-    def __init__(self, request):
-        self.domain = self.name = request.get_host()
-
-    def __str__(self):
-        return self.domain
-
-    def save(self, force_insert=False, force_update=False):
-        raise NotImplementedError('RequestSite cannot be saved.')
-
-    def delete(self):
-        raise NotImplementedError('RequestSite cannot be deleted.')
+class RequestSite(RealRequestSite):
+    def __init__(self, *args, **kwargs):
+        warnings.warn(
+            "Please import RequestSite from django.contrib.sites.requests.",
+            PendingDeprecationWarning, stacklevel=2)
+        super(RequestSite, self).__init__(*args, **kwargs)
 
 
 def get_current_site(request):
-    """
-    Checks if contrib.sites is installed and returns either the current
-    ``Site`` object or a ``RequestSite`` object based on the request.
-    """
-    if Site._meta.installed:
-        current_site = Site.objects.get_current()
-    else:
-        current_site = RequestSite(request)
-    return current_site
+    warnings.warn(
+        "Please import get_current_site from django.contrib.sites.shortcuts.",
+        PendingDeprecationWarning, stacklevel=2)
+    return real_get_current_site(request)
 
 
 def clear_site_cache(sender, **kwargs):

+ 25 - 0
django/contrib/sites/requests.py

@@ -0,0 +1,25 @@
+from __future__ import unicode_literals
+
+from django.utils.encoding import python_2_unicode_compatible
+
+
+@python_2_unicode_compatible
+class RequestSite(object):
+    """
+    A class that shares the primary interface of Site (i.e., it has
+    ``domain`` and ``name`` attributes) but gets its data from a Django
+    HttpRequest object rather than from a database.
+
+    The save() and delete() methods raise NotImplementedError.
+    """
+    def __init__(self, request):
+        self.domain = self.name = request.get_host()
+
+    def __str__(self):
+        return self.domain
+
+    def save(self, force_insert=False, force_update=False):
+        raise NotImplementedError('RequestSite cannot be saved.')
+
+    def delete(self):
+        raise NotImplementedError('RequestSite cannot be deleted.')

+ 18 - 0
django/contrib/sites/shortcuts.py

@@ -0,0 +1,18 @@
+from __future__ import unicode_literals
+
+from django.apps import apps
+
+
+def get_current_site(request):
+    """
+    Checks if contrib.sites is installed and returns either the current
+    ``Site`` object or a ``RequestSite`` object based on the request.
+    """
+    # Imports are inside the function because its point is to avoid importing
+    # the Site models when django.contrib.sites isn't installed.
+    if apps.is_installed('django.contrib.sites'):
+        from .models import Site
+        return Site.objects.get_current()
+    else:
+        from .requests import RequestSite
+        return RequestSite(request)

+ 4 - 1
django/contrib/sites/tests.py

@@ -1,11 +1,14 @@
 from __future__ import unicode_literals
 
 from django.conf import settings
-from django.contrib.sites.models import Site, RequestSite, get_current_site
 from django.core.exceptions import ObjectDoesNotExist, ValidationError
 from django.http import HttpRequest
 from django.test import TestCase, modify_settings, override_settings
 
+from .models import Site
+from .requests import RequestSite
+from .shortcuts import get_current_site
+
 
 @modify_settings(INSTALLED_APPS={'append': 'django.contrib.sites'})
 class SitesFrameworkTests(TestCase):

+ 1 - 1
django/contrib/syndication/views.py

@@ -3,7 +3,7 @@ from __future__ import unicode_literals
 from calendar import timegm
 
 from django.conf import settings
-from django.contrib.sites.models import get_current_site
+from django.contrib.sites.shortcuts import get_current_site
 from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
 from django.http import HttpResponse, Http404
 from django.template import loader, TemplateDoesNotExist, RequestContext

+ 1 - 1
django/views/decorators/cache.py

@@ -12,7 +12,7 @@ def cache_page(*args, **kwargs):
     The cache is keyed by the URL and some data from the headers.
     Additionally there is the key prefix that is used to distinguish different
     cache areas in a multi-site setup. You could use the
-    sites.get_current_site().domain, for example, as that is unique across a Django
+    get_current_site().domain, for example, as that is unique across a Django
     project.
 
     Additionally, all headers from the response's Vary header will be taken

+ 3 - 0
docs/internals/deprecation.txt

@@ -184,6 +184,9 @@ these changes.
 
 * ``AppCommand.handle_app()`` will no longer be supported.
 
+* ``RequestSite`` will be located in ``django.contrib.sites.requests`` and
+  ``get_current_site`` in ``django.contrib.sites.shortcuts``.
+
 * FastCGI support via the ``runfcgi`` management command will be
   removed. Please deploy your project using WSGI.
 

+ 41 - 29
docs/ref/contrib/sites.txt

@@ -6,8 +6,6 @@ The "sites" framework
    :synopsis: Lets you operate multiple Web sites from the same database and
               Django project
 
-.. currentmodule:: django.contrib.sites.models
-
 Django comes with an optional "sites" framework. It's a hook for associating
 objects and functionality to particular Web sites, and it's a holding place for
 the domain names and "verbose" names of your Django-powered sites.
@@ -15,9 +13,9 @@ the domain names and "verbose" names of your Django-powered sites.
 Use it if your single Django installation powers more than one site and you
 need to differentiate between those sites in some way.
 
-The whole sites framework is based on a simple model:
+The sites framework is mainly based on a simple model:
 
-.. class:: Site
+.. class:: models.Site
 
     A model for storing the ``domain`` and ``name`` attributes of a Web site.
     The :setting:`SITE_ID` setting specifies the database ID of the
@@ -32,7 +30,6 @@ The whole sites framework is based on a simple model:
 
         A human-readable "verbose" name for the Web site.
 
-
 How you use this is up to you, but Django uses it in a couple of ways
 automatically via simple conventions.
 
@@ -80,7 +77,7 @@ This accomplishes several things quite nicely:
   The view code that displays a given story just checks to make sure the
   requested story is on the current site. It looks something like this::
 
-      from django.contrib.sites.models import get_current_site
+      from django.contrib.sites.shortcuts import get_current_site
 
       def article_detail(request, article_id):
           try:
@@ -137,7 +134,7 @@ hard-coding is best for hackish fixes that you need done quickly. The
 cleaner way of accomplishing the same thing is to check the current site's
 domain::
 
-    from django.contrib.sites.models import get_current_site
+    from django.contrib.sites.shortcuts import get_current_site
 
     def my_view(request):
         current_site = get_current_site(request)
@@ -149,7 +146,8 @@ domain::
             pass
 
 This has also the advantage of checking if the sites framework is installed,
-and return a :class:`RequestSite` instance if it is not.
+and return a :class:`~django.contrib.sites.requests.RequestSite` instance if
+it is not.
 
 If you don't have access to the request object, you can use the
 ``get_current()`` method of the :class:`~django.contrib.sites.models.Site`
@@ -185,7 +183,7 @@ current site's :attr:`~django.contrib.sites.models.Site.name` and
 
 Here's an example of what the form-handling view looks like::
 
-    from django.contrib.sites.models import get_current_site
+    from django.contrib.sites.shortcuts import get_current_site
     from django.core.mail import send_mail
 
     def register_for_newsletter(request):
@@ -296,12 +294,10 @@ clear the cache using ``Site.objects.clear_cache()``::
     Site.objects.clear_cache()
     current_site = Site.objects.get_current()
 
-.. currentmodule:: django.contrib.sites.managers
-
 The ``CurrentSiteManager``
 ==========================
 
-.. class:: CurrentSiteManager
+.. class:: managers.CurrentSiteManager
 
 If :class:`~django.contrib.sites.models.Site` plays a key role in your
 application, consider using the helpful
@@ -426,8 +422,6 @@ Here's how Django uses the sites framework:
   :class:`~django.contrib.sites.models.Site` to work out the domain for the
   site that it will redirect to.
 
-.. currentmodule:: django.contrib.sites.models
-
 ``RequestSite`` objects
 =======================
 
@@ -435,32 +429,50 @@ Here's how Django uses the sites framework:
 
 Some :doc:`django.contrib </ref/contrib/index>` applications take advantage of
 the sites framework but are architected in a way that doesn't *require* the
-sites framework to be installed in your database. (Some people don't want to, or
-just aren't *able* to install the extra database table that the sites framework
-requires.) For those cases, the framework provides a
-:class:`~django.contrib.sites.models.RequestSite` class, which can be used as a
-fallback when the database-backed sites framework is not available.
+sites framework to be installed in your database. (Some people don't want to,
+or just aren't *able* to install the extra database table that the sites
+framework requires.) For those cases, the framework provides a
+:class:`django.contrib.sites.requests.RequestSite` class, which can be used as
+a fallback when the database-backed sites framework is not available.
 
-.. class:: RequestSite
+.. class:: requests.RequestSite
 
     A class that shares the primary interface of
     :class:`~django.contrib.sites.models.Site` (i.e., it has
     ``domain`` and ``name`` attributes) but gets its data from a Django
     :class:`~django.http.HttpRequest` object rather than from a database.
 
-    The ``save()`` and ``delete()`` methods raise ``NotImplementedError``.
-
     .. method:: __init__(request)
 
         Sets the ``name`` and ``domain`` attributes to the value of
         :meth:`~django.http.HttpRequest.get_host`.
 
+    .. versionchanged:: 1.7
+
+      This class used to be defined in ``django.contrib.sites.models``.
 
-A :class:`~django.contrib.sites.models.RequestSite` object has a similar
-interface to a normal :class:`~django.contrib.sites.models.Site` object, except
-its :meth:`~django.contrib.sites.models.RequestSite.__init__()` method takes an
-:class:`~django.http.HttpRequest` object. It's able to deduce the
-``domain`` and ``name`` by looking at the request's domain. It has ``save()``
-and ``delete()`` methods to match the interface of
+A :class:`~django.contrib.sites.requests.RequestSite` object has a similar
+interface to a normal :class:`~django.contrib.sites.models.Site` object,
+except its :meth:`~django.contrib.sites.requests.RequestSite.__init__()`
+method takes an :class:`~django.http.HttpRequest` object. It's able to deduce
+the ``domain`` and ``name`` by looking at the request's domain. It has
+``save()`` and ``delete()`` methods to match the interface of
 :class:`~django.contrib.sites.models.Site`, but the methods raise
-``NotImplementedError``.
+:exc:`~exceptions.NotImplementedError`..
+
+``get_current_site`` shortcut
+=============================
+
+Finally, to avoid repetitive fallback code, the framework provides a
+:func:`django.contrib.sites.shortcut.get_current_site` function.
+
+.. function:: shortcuts.get_current_site
+
+    A function that checks if ``django.contrib.sites`` is installed and
+    returns either the current :class:`~django.contrib.sites.models.Site`
+    object or a :class:`~django.contrib.sites.requests.RequestSite` object
+    based on the request.
+
+    .. versionchanged:: 1.7
+
+      This function used to be defined in ``django.contrib.sites.models``.

+ 1 - 1
docs/ref/contrib/syndication.txt

@@ -131,7 +131,7 @@ into those elements.
     representing the current site. This is useful for ``{{ site.domain
     }}`` or ``{{ site.name }}``. If you do *not* have the Django sites
     framework installed, this will be set to a
-    :class:`django.contrib.sites.models.RequestSite` object. See the
+    :class:`~django.contrib.sites.requests.RequestSite` object. See the
     :ref:`RequestSite section of the sites framework documentation
     <requestsite-objects>` for more.
 

+ 13 - 0
docs/releases/1.7.txt

@@ -1009,6 +1009,19 @@ than simply ``myapp/models.py``, Django would look for :ref:`initial SQL data
 will search ``myapp/sql/`` as documented. The old location will continue to
 work until Django 1.9.
 
+Reorganization of ``django.contrib.sites``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``django.contrib.sites`` provides reduced functionality when it isn't in
+:setting:`INSTALLED_APPS`. The app-loading refactor adds some constraints in
+that situation. As a consequence, two objects were moved, and the old
+locations are deprecated:
+
+* :class:`~django.contrib.sites.requests.RequestSite` now lives in
+  ``django.contrib.sites.requests``.
+* :func:`~django.contrib.sites.shortcuts.get_current_site` now lives in
+  ``django.contrib.sites.shortcuts``.
+
 ``declared_fieldsets`` attribute on ``ModelAdmin``
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

+ 2 - 2
docs/topics/auth/default.txt

@@ -648,7 +648,7 @@ patterns.
     * ``site``: The current :class:`~django.contrib.sites.models.Site`,
       according to the :setting:`SITE_ID` setting. If you don't have the
       site framework installed, this will be set to an instance of
-      :class:`~django.contrib.sites.models.RequestSite`, which derives the
+      :class:`~django.contrib.sites.requests.RequestSite`, which derives the
       site name and domain from the current
       :class:`~django.http.HttpRequest`.
 
@@ -744,7 +744,7 @@ patterns.
     * ``site``: The current :class:`~django.contrib.sites.models.Site`,
       according to the :setting:`SITE_ID` setting. If you don't have the
       site framework installed, this will be set to an instance of
-      :class:`~django.contrib.sites.models.RequestSite`, which derives the
+      :class:`~django.contrib.sites.requests.RequestSite`, which derives the
       site name and domain from the current
       :class:`~django.http.HttpRequest`.