Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.2.X] Fixed #14386, #8960, #10235, #10909, #10608, #13845, #14377 -…

… standardize Site/RequestSite usage in various places.

Many thanks to gabrielhurley for putting most of this together.  Also to
bmihelac, arthurk, qingfeng, hvendelbo, petr.pulc@s-cape.cz, Hraban for
reports and some initial patches.

The patch also contains some whitespace/PEP8 fixes.

Backport of [13980] from trunk. Some items could be considered features
(i.e.  supporting RequestSite in various contrib apps), others are definite
bugs, but it was much more robust to backport all these tightly related
changes together.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.2.X@13987 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 12187bfd26fa8205d5a6a12bdf294c0a1fc21a6c 1 parent c7ce5b8
Luke Plant authored October 05, 2010
6  django/contrib/auth/forms.py
... ...
@@ -1,7 +1,7 @@
1 1
 from django.contrib.auth.models import User
2 2
 from django.contrib.auth import authenticate
3 3
 from django.contrib.auth.tokens import default_token_generator
4  
-from django.contrib.sites.models import Site
  4
+from django.contrib.sites.models import get_current_site
5 5
 from django.template import Context, loader
6 6
 from django import forms
7 7
 from django.utils.translation import ugettext_lazy as _
@@ -117,14 +117,14 @@ def clean_email(self):
117 117
         return email
118 118
 
119 119
     def save(self, domain_override=None, email_template_name='registration/password_reset_email.html',
120  
-             use_https=False, token_generator=default_token_generator):
  120
+             use_https=False, token_generator=default_token_generator, request=None):
121 121
         """
122 122
         Generates a one-use only link for resetting password and sends to the user
123 123
         """
124 124
         from django.core.mail import send_mail
125 125
         for user in self.users_cache:
126 126
             if not domain_override:
127  
-                current_site = Site.objects.get_current()
  127
+                current_site = get_current_site(request)
128 128
                 site_name = current_site.name
129 129
                 domain = current_site.domain
130 130
             else:
6  django/contrib/auth/tests/views.py
@@ -249,6 +249,12 @@ def test_logout_default(self):
249 249
         self.assert_('Logged out' in response.content)
250 250
         self.confirm_logged_out()
251 251
 
  252
+    def test_14377(self):
  253
+        # Bug 14377
  254
+        self.login()
  255
+        response = self.client.get('/logout/')
  256
+        self.assertTrue('site' in response.context)
  257
+
252 258
     def test_logout_with_next_page_specified(self): 
253 259
         "Logout with next_page option given redirects to specified resource"
254 260
         self.login()
34  django/contrib/auth/views.py
@@ -10,7 +10,7 @@
10 10
 from django.views.decorators.csrf import csrf_protect
11 11
 from django.core.urlresolvers import reverse
12 12
 from django.shortcuts import render_to_response, get_object_or_404
13  
-from django.contrib.sites.models import Site, RequestSite
  13
+from django.contrib.sites.models import get_current_site
14 14
 from django.http import HttpResponseRedirect, Http404
15 15
 from django.template import RequestContext
16 16
 from django.utils.http import urlquote, base36_to_int
@@ -26,21 +26,21 @@ def login(request, template_name='registration/login.html',
26 26
     """Displays the login form and handles the login action."""
27 27
 
28 28
     redirect_to = request.REQUEST.get(redirect_field_name, '')
29  
-    
  29
+
30 30
     if request.method == "POST":
31 31
         form = authentication_form(data=request.POST)
32 32
         if form.is_valid():
33 33
             # Light security check -- make sure redirect_to isn't garbage.
34 34
             if not redirect_to or ' ' in redirect_to:
35 35
                 redirect_to = settings.LOGIN_REDIRECT_URL
36  
-            
37  
-            # Heavier security check -- redirects to http://example.com should 
38  
-            # not be allowed, but things like /view/?param=http://example.com 
  36
+
  37
+            # Heavier security check -- redirects to http://example.com should
  38
+            # not be allowed, but things like /view/?param=http://example.com
39 39
             # should be allowed. This regex checks if there is a '//' *before* a
40 40
             # question mark.
41 41
             elif '//' in redirect_to and re.match(r'[^\?]*//', redirect_to):
42 42
                     redirect_to = settings.LOGIN_REDIRECT_URL
43  
-            
  43
+
44 44
             # Okay, security checks complete. Log the user in.
45 45
             auth_login(request, form.get_user())
46 46
 
@@ -51,14 +51,11 @@ def login(request, template_name='registration/login.html',
51 51
 
52 52
     else:
53 53
         form = authentication_form(request)
54  
-    
  54
+
55 55
     request.session.set_test_cookie()
56  
-    
57  
-    if Site._meta.installed:
58  
-        current_site = Site.objects.get_current()
59  
-    else:
60  
-        current_site = RequestSite(request)
61  
-    
  56
+
  57
+    current_site = get_current_site(request)
  58
+
62 59
     return render_to_response(template_name, {
63 60
         'form': form,
64 61
         redirect_field_name: redirect_to,
@@ -75,7 +72,10 @@ def logout(request, next_page=None, template_name='registration/logged_out.html'
75 72
         if redirect_to:
76 73
             return HttpResponseRedirect(redirect_to)
77 74
         else:
  75
+            current_site = get_current_site(request)
78 76
             return render_to_response(template_name, {
  77
+                'site': current_site,
  78
+                'site_name': current_site.name,
79 79
                 'title': _('Logged out')
80 80
             }, context_instance=RequestContext(request))
81 81
     else:
@@ -97,7 +97,7 @@ def redirect_to_login(next, login_url=None, redirect_field_name=REDIRECT_FIELD_N
97 97
 # 4 views for password reset:
98 98
 # - password_reset sends the mail
99 99
 # - password_reset_done shows a success message for the above
100  
-# - password_reset_confirm checks the link the user clicked and 
  100
+# - password_reset_confirm checks the link the user clicked and
101 101
 #   prompts for a new password
102 102
 # - password_reset_complete shows a success message for the above
103 103
 
@@ -114,12 +114,10 @@ def password_reset(request, is_admin_site=False, template_name='registration/pas
114 114
             opts = {}
115 115
             opts['use_https'] = request.is_secure()
116 116
             opts['token_generator'] = token_generator
  117
+            opts['email_template_name'] = email_template_name
  118
+            opts['request'] = request
117 119
             if is_admin_site:
118 120
                 opts['domain_override'] = request.META['HTTP_HOST']
119  
-            else:
120  
-                opts['email_template_name'] = email_template_name
121  
-                if not Site._meta.installed:
122  
-                    opts['domain_override'] = RequestSite(request).domain
123 121
             form.save(**opts)
124 122
             return HttpResponseRedirect(post_reset_redirect)
125 123
     else:
96  django/contrib/contenttypes/tests.py
... ...
@@ -1,47 +1,69 @@
1  
-"""
2  
-Make sure that the content type cache (see ContentTypeManager) works correctly.
3  
-Lookups for a particular content type -- by model or by ID -- should hit the
4  
-database only on the first lookup.
  1
+from django import db
  2
+from django.conf import settings
  3
+from django.contrib.contenttypes.models import ContentType
  4
+from django.contrib.sites.models import Site
  5
+from django.contrib.contenttypes.views import shortcut
  6
+from django.core.exceptions import ObjectDoesNotExist
  7
+from django.http import HttpRequest
  8
+from django.test import TestCase
5 9
 
6  
-First, let's make sure we're dealing with a blank slate (and that DEBUG is on so
7  
-that queries get logged)::
8 10
 
9  
-    >>> from django.conf import settings
10  
-    >>> settings.DEBUG = True
  11
+class ContentTypesTests(TestCase):
11 12
 
12  
-    >>> from django.contrib.contenttypes.models import ContentType
13  
-    >>> ContentType.objects.clear_cache()
  13
+    def setUp(self):
  14
+        # First, let's make sure we're dealing with a blank slate (and that
  15
+        # DEBUG is on so that queries get logged)
  16
+        self.old_DEBUG = settings.DEBUG
  17
+        self.old_Site_meta_installed = Site._meta.installed
  18
+        settings.DEBUG = True
  19
+        ContentType.objects.clear_cache()
  20
+        db.reset_queries()
14 21
 
15  
-    >>> from django import db
16  
-    >>> db.reset_queries()
17  
-    
18  
-At this point, a lookup for a ContentType should hit the DB::
  22
+    def tearDown(self):
  23
+        settings.DEBUG = self.old_DEBUG
  24
+        Site._meta.installed = self.old_Site_meta_installed
19 25
 
20  
-    >>> ContentType.objects.get_for_model(ContentType)
21  
-    <ContentType: content type>
22  
-    
23  
-    >>> len(db.connection.queries)
24  
-    1
  26
+    def test_lookup_cache(self):
  27
+        """
  28
+        Make sure that the content type cache (see ContentTypeManager)
  29
+        works correctly. Lookups for a particular content type -- by model or
  30
+        by ID -- should hit the database only on the first lookup.
  31
+        """
25 32
 
26  
-A second hit, though, won't hit the DB, nor will a lookup by ID::
  33
+        # At this point, a lookup for a ContentType should hit the DB
  34
+        ContentType.objects.get_for_model(ContentType)
  35
+        self.assertEqual(1, len(db.connection.queries))
27 36
 
28  
-    >>> ct = ContentType.objects.get_for_model(ContentType)
29  
-    >>> len(db.connection.queries)
30  
-    1
31  
-    >>> ContentType.objects.get_for_id(ct.id)
32  
-    <ContentType: content type>
33  
-    >>> len(db.connection.queries)
34  
-    1
  37
+        # A second hit, though, won't hit the DB, nor will a lookup by ID
  38
+        ct = ContentType.objects.get_for_model(ContentType)
  39
+        self.assertEqual(1, len(db.connection.queries))
  40
+        ContentType.objects.get_for_id(ct.id)
  41
+        self.assertEqual(1, len(db.connection.queries))
35 42
 
36  
-Once we clear the cache, another lookup will again hit the DB::
  43
+        # Once we clear the cache, another lookup will again hit the DB
  44
+        ContentType.objects.clear_cache()
  45
+        ContentType.objects.get_for_model(ContentType)
  46
+        len(db.connection.queries)
  47
+        self.assertEqual(2, len(db.connection.queries))
37 48
 
38  
-    >>> ContentType.objects.clear_cache()
39  
-    >>> ContentType.objects.get_for_model(ContentType)
40  
-    <ContentType: content type>
41  
-    >>> len(db.connection.queries)
42  
-    2
  49
+    def test_shortcut_view(self):
  50
+        """
  51
+        Check that the shortcut view (used for the admin "view on site"
  52
+        functionality) returns a complete URL regardless of whether the sites
  53
+        framework is installed
  54
+        """
43 55
 
44  
-Don't forget to reset DEBUG!
45  
-
46  
-    >>> settings.DEBUG = False
47  
-"""
  56
+        request = HttpRequest()
  57
+        request.META = {
  58
+            "SERVER_NAME": "Example.com",
  59
+            "SERVER_PORT": "80",
  60
+        }
  61
+        from django.contrib.auth.models import User
  62
+        user_ct = ContentType.objects.get_for_model(User)
  63
+        obj = User.objects.create(username="john")
  64
+        Site._meta.installed = True
  65
+        response = shortcut(request, user_ct.id, obj.id)
  66
+        self.assertEqual("http://example.com/users/john/", response._headers.get("location")[1])
  67
+        Site._meta.installed = False
  68
+        response = shortcut(request, user_ct.id, obj.id)
  69
+        self.assertEqual("http://Example.com/users/john/", response._headers.get("location")[1])
42  django/contrib/contenttypes/views.py
... ...
@@ -1,6 +1,6 @@
1 1
 from django import http
2 2
 from django.contrib.contenttypes.models import ContentType
3  
-from django.contrib.sites.models import Site
  3
+from django.contrib.sites.models import Site, get_current_site
4 4
 from django.core.exceptions import ObjectDoesNotExist
5 5
 
6 6
 def shortcut(request, content_type_id, object_id):
@@ -26,35 +26,37 @@ def shortcut(request, content_type_id, object_id):
26 26
     # Otherwise, we need to introspect the object's relationships for a
27 27
     # relation to the Site object
28 28
     object_domain = None
29  
-    opts = obj._meta
30 29
 
31  
-    # First, look for an many-to-many relationship to Site.
32  
-    for field in opts.many_to_many:
33  
-        if field.rel.to is Site:
34  
-            try:
35  
-                # Caveat: In the case of multiple related Sites, this just
36  
-                # selects the *first* one, which is arbitrary.
37  
-                object_domain = getattr(obj, field.name).all()[0].domain
38  
-            except IndexError:
39  
-                pass
40  
-            if object_domain is not None:
41  
-                break
  30
+    if Site._meta.installed:
  31
+        opts = obj._meta
42 32
 
43  
-    # Next, look for a many-to-one relationship to Site.
44  
-    if object_domain is None:
45  
-        for field in obj._meta.fields:
46  
-            if field.rel and field.rel.to is Site:
  33
+        # First, look for an many-to-many relationship to Site.
  34
+        for field in opts.many_to_many:
  35
+            if field.rel.to is Site:
47 36
                 try:
48  
-                    object_domain = getattr(obj, field.name).domain
49  
-                except Site.DoesNotExist:
  37
+                    # Caveat: In the case of multiple related Sites, this just
  38
+                    # selects the *first* one, which is arbitrary.
  39
+                    object_domain = getattr(obj, field.name).all()[0].domain
  40
+                except IndexError:
50 41
                     pass
51 42
                 if object_domain is not None:
52 43
                     break
53 44
 
  45
+        # Next, look for a many-to-one relationship to Site.
  46
+        if object_domain is None:
  47
+            for field in obj._meta.fields:
  48
+                if field.rel and field.rel.to is Site:
  49
+                    try:
  50
+                        object_domain = getattr(obj, field.name).domain
  51
+                    except Site.DoesNotExist:
  52
+                        pass
  53
+                    if object_domain is not None:
  54
+                        break
  55
+
54 56
     # Fall back to the current site (if possible).
55 57
     if object_domain is None:
56 58
         try:
57  
-            object_domain = Site.objects.get_current().domain
  59
+            object_domain = get_current_site(request).domain
58 60
         except Site.DoesNotExist:
59 61
             pass
60 62
 
4  django/contrib/gis/sitemaps/views.py
... ...
@@ -1,6 +1,6 @@
1 1
 from django.http import HttpResponse, Http404
2 2
 from django.template import loader
3  
-from django.contrib.sites.models import Site
  3
+from django.contrib.sites.models import get_current_site
4 4
 from django.core import urlresolvers
5 5
 from django.core.paginator import EmptyPage, PageNotAnInteger
6 6
 from django.contrib.gis.db.models.fields import GeometryField
@@ -15,7 +15,7 @@ def index(request, sitemaps):
15 15
     This view generates a sitemap index that uses the proper view
16 16
     for resolving geographic section sitemap URLs.
17 17
     """
18  
-    current_site = Site.objects.get_current()
  18
+    current_site = get_current_site(request)
19 19
     sites = []
20 20
     protocol = request.is_secure() and 'https' or 'http'
21 21
     for section, site in sitemaps.items():
12  django/contrib/sitemaps/__init__.py
... ...
@@ -1,3 +1,4 @@
  1
+from django.contrib.sites.models import get_current_site
1 2
 from django.core import urlresolvers, paginator
2 3
 import urllib
3 4
 
@@ -60,8 +61,7 @@ def _get_paginator(self):
60 61
     paginator = property(_get_paginator)
61 62
 
62 63
     def get_urls(self, page=1):
63  
-        from django.contrib.sites.models import Site
64  
-        current_site = Site.objects.get_current()
  64
+        current_site = get_current_site(self.request)
65 65
         urls = []
66 66
         for item in self.paginator.page(page).object_list:
67 67
             loc = "http://%s%s" % (current_site.domain, self.__get('location', item))
@@ -77,9 +77,11 @@ def get_urls(self, page=1):
77 77
 
78 78
 class FlatPageSitemap(Sitemap):
79 79
     def items(self):
80  
-        from django.contrib.sites.models import Site
81  
-        current_site = Site.objects.get_current()
82  
-        return current_site.flatpage_set.filter(registration_required=False)
  80
+        current_site = get_current_site(self.request)
  81
+        if hasattr(current_site, "flatpage_set"):
  82
+            return current_site.flatpage_set.filter(registration_required=False)
  83
+        else:
  84
+            return ()
83 85
 
84 86
 class GenericSitemap(Sitemap):
85 87
     priority = None
18  django/contrib/sitemaps/tests/basic.py
@@ -2,6 +2,7 @@
2 2
 from django.conf import settings
3 3
 from django.contrib.auth.models import User
4 4
 from django.contrib.flatpages.models import FlatPage
  5
+from django.contrib.sites.models import Site
5 6
 from django.test import TestCase
6 7
 from django.utils.formats import localize
7 8
 from django.utils.translation import activate
@@ -12,11 +13,13 @@ class SitemapTests(TestCase):
12 13
 
13 14
     def setUp(self):
14 15
         self.old_USE_L10N = settings.USE_L10N
  16
+        self.old_Site_meta_installed = Site._meta.installed
15 17
         # Create a user that will double as sitemap content
16 18
         User.objects.create_user('testuser', 'test@example.com', 's3krit')
17 19
 
18 20
     def tearDown(self):
19 21
         settings.USE_L10N = self.old_USE_L10N
  22
+        Site._meta.installed = self.old_Site_meta_installed
20 23
 
21 24
     def test_simple_sitemap(self):
22 25
         "A simple sitemap can be rendered"
@@ -66,7 +69,7 @@ def test_flatpage_sitemap(self):
66 69
             url=u'/private/',
67 70
             title=u'Public Page',
68 71
             enable_comments=True,
69  
-            registration_required=True    
  72
+            registration_required=True
70 73
         )
71 74
         private.sites.add(settings.SITE_ID)
72 75
         response = self.client.get('/flatpages/sitemap.xml')
@@ -75,3 +78,16 @@ def test_flatpage_sitemap(self):
75 78
         # Private flatpage should not be in the sitemap
76 79
         self.assertNotContains(response, '<loc>http://example.com%s</loc>' % private.url)
77 80
 
  81
+    def test_requestsite_sitemap(self):
  82
+        # Make sure hitting the flatpages sitemap without the sites framework
  83
+        # installed doesn't raise an exception
  84
+        Site._meta.installed = False
  85
+        response = self.client.get('/flatpages/sitemap.xml')
  86
+        # Retrieve the sitemap.
  87
+        response = self.client.get('/simple/sitemap.xml')
  88
+        # Check for all the important bits:
  89
+        self.assertEquals(response.content, """<?xml version="1.0" encoding="UTF-8"?>
  90
+<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  91
+<url><loc>http://testserver/location/</loc><lastmod>%s</lastmod><changefreq>never</changefreq><priority>0.5</priority></url>
  92
+</urlset>
  93
+""" % date.today().strftime('%Y-%m-%d'))
6  django/contrib/sitemaps/views.py
... ...
@@ -1,15 +1,16 @@
1 1
 from django.http import HttpResponse, Http404
2 2
 from django.template import loader
3  
-from django.contrib.sites.models import Site
  3
+from django.contrib.sites.models import get_current_site
4 4
 from django.core import urlresolvers
5 5
 from django.utils.encoding import smart_str
6 6
 from django.core.paginator import EmptyPage, PageNotAnInteger
7 7
 
8 8
 def index(request, sitemaps):
9  
-    current_site = Site.objects.get_current()
  9
+    current_site = get_current_site(request)
10 10
     sites = []
11 11
     protocol = request.is_secure() and 'https' or 'http'
12 12
     for section, site in sitemaps.items():
  13
+        site.request = request
13 14
         if callable(site):
14 15
             pages = site().paginator.num_pages
15 16
         else:
@@ -32,6 +33,7 @@ def sitemap(request, sitemaps, section=None):
32 33
         maps = sitemaps.values()
33 34
     page = request.GET.get("p", 1)
34 35
     for site in maps:
  36
+        site.request = request
35 37
         try:
36 38
             if callable(site):
37 39
                 urls.extend(site().get_urls(page))
18  django/contrib/sites/models.py
... ...
@@ -1,9 +1,12 @@
1 1
 from django.db import models
2 2
 from django.utils.translation import ugettext_lazy as _
3 3
 
  4
+
4 5
 SITE_CACHE = {}
5 6
 
  7
+
6 8
 class SiteManager(models.Manager):
  9
+
7 10
     def get_current(self):
8 11
         """
9 12
         Returns the current ``Site`` based on the SITE_ID in the
@@ -28,7 +31,9 @@ def clear_cache(self):
28 31
         global SITE_CACHE
29 32
         SITE_CACHE = {}
30 33
 
  34
+
31 35
 class Site(models.Model):
  36
+
32 37
     domain = models.CharField(_('domain name'), max_length=100)
33 38
     name = models.CharField(_('display name'), max_length=50)
34 39
     objects = SiteManager()
@@ -56,6 +61,7 @@ def delete(self):
56 61
         except KeyError:
57 62
             pass
58 63
 
  64
+
59 65
 class RequestSite(object):
60 66
     """
61 67
     A class that shares the primary interface of Site (i.e., it has
@@ -75,3 +81,15 @@ def save(self, force_insert=False, force_update=False):
75 81
 
76 82
     def delete(self):
77 83
         raise NotImplementedError('RequestSite cannot be deleted.')
  84
+
  85
+
  86
+def get_current_site(request):
  87
+    """
  88
+    Checks if contrib.sites is installed and returns either the current
  89
+    ``Site`` object or a ``RequestSite`` object based on the request.
  90
+    """
  91
+    if Site._meta.installed:
  92
+        current_site = Site.objects.get_current()
  93
+    else:
  94
+        current_site = RequestSite(request)
  95
+    return current_site
85  django/contrib/sites/tests.py
... ...
@@ -1,29 +1,56 @@
1  
-"""
2  
->>> from django.contrib.sites.models import Site
3  
->>> from django.conf import settings
4  
->>> Site(id=settings.SITE_ID, domain="example.com", name="example.com").save()
5  
-
6  
-# Make sure that get_current() does not return a deleted Site object.
7  
->>> s = Site.objects.get_current()
8  
->>> isinstance(s, Site)
9  
-True
10  
-
11  
->>> s.delete()
12  
->>> Site.objects.get_current()
13  
-Traceback (most recent call last):
14  
-...
15  
-DoesNotExist: Site matching query does not exist.
16  
-
17  
-# After updating a Site object (e.g. via the admin), we shouldn't return a
18  
-# bogus value from the SITE_CACHE.
19  
->>> _ = Site.objects.create(id=settings.SITE_ID, domain="example.com", name="example.com")
20  
->>> site = Site.objects.get_current()
21  
->>> site.name
22  
-u"example.com"
23  
->>> s2 = Site.objects.get(id=settings.SITE_ID)
24  
->>> s2.name = "Example site"
25  
->>> s2.save()
26  
->>> site = Site.objects.get_current()
27  
->>> site.name
28  
-u"Example site"
29  
-"""
  1
+from django.conf import settings
  2
+from django.contrib.sites.models import Site, RequestSite, get_current_site
  3
+from django.core.exceptions import ObjectDoesNotExist
  4
+from django.http import HttpRequest
  5
+from django.test import TestCase
  6
+
  7
+
  8
+class SitesFrameworkTests(TestCase):
  9
+
  10
+    def setUp(self):
  11
+        Site(id=settings.SITE_ID, domain="example.com", name="example.com").save()
  12
+        self.old_Site_meta_installed = Site._meta.installed
  13
+        Site._meta.installed = True
  14
+
  15
+    def tearDown(self):
  16
+        Site._meta.installed = self.old_Site_meta_installed
  17
+
  18
+    def test_site_manager(self):
  19
+        # Make sure that get_current() does not return a deleted Site object.
  20
+        s = Site.objects.get_current()
  21
+        self.assert_(isinstance(s, Site))
  22
+        s.delete()
  23
+        self.assertRaises(ObjectDoesNotExist, Site.objects.get_current)
  24
+
  25
+    def test_site_cache(self):
  26
+        # After updating a Site object (e.g. via the admin), we shouldn't return a
  27
+        # bogus value from the SITE_CACHE.
  28
+        site = Site.objects.get_current()
  29
+        self.assertEqual(u"example.com", site.name)
  30
+        s2 = Site.objects.get(id=settings.SITE_ID)
  31
+        s2.name = "Example site"
  32
+        s2.save()
  33
+        site = Site.objects.get_current()
  34
+        self.assertEqual(u"Example site", site.name)
  35
+
  36
+    def test_get_current_site(self):
  37
+        # Test that the correct Site object is returned
  38
+        request = HttpRequest()
  39
+        request.META = {
  40
+            "SERVER_NAME": "example.com",
  41
+            "SERVER_PORT": "80",
  42
+        }
  43
+        site = get_current_site(request)
  44
+        self.assert_(isinstance(site, Site))
  45
+        self.assertEqual(site.id, settings.SITE_ID)
  46
+
  47
+        # Test that an exception is raised if the sites framework is installed
  48
+        # but there is no matching Site
  49
+        site.delete()
  50
+        self.assertRaises(ObjectDoesNotExist, get_current_site, request)
  51
+
  52
+        # A RequestSite is returned if the sites framework is not installed
  53
+        Site._meta.installed = False
  54
+        site = get_current_site(request)
  55
+        self.assert_(isinstance(site, RequestSite))
  56
+        self.assertEqual(site.name, u"example.com")
7  django/contrib/syndication/views.py
... ...
@@ -1,6 +1,6 @@
1 1
 import datetime
2 2
 from django.conf import settings
3  
-from django.contrib.sites.models import Site, RequestSite
  3
+from django.contrib.sites.models import get_current_site
4 4
 from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
5 5
 from django.http import HttpResponse, Http404
6 6
 from django.template import loader, Template, TemplateDoesNotExist, RequestContext
@@ -91,10 +91,7 @@ def get_feed(self, obj, request):
91 91
         Returns a feedgenerator.DefaultFeed object, fully populated, for
92 92
         this feed. Raises FeedDoesNotExist for invalid parameters.
93 93
         """
94  
-        if Site._meta.installed:
95  
-            current_site = Site.objects.get_current()
96  
-        else:
97  
-            current_site = RequestSite(request)
  94
+        current_site = get_current_site(request)
98 95
 
99 96
         link = self.__get_dynamic_attr('link', obj)
100 97
         link = add_domain(current_site.domain, link)
12  docs/ref/contrib/sites.txt
@@ -105,7 +105,7 @@ This has the same benefits as described in the last section.
105 105
 Hooking into the current site from views
106 106
 ----------------------------------------
107 107
 
108  
-On a lower level, you can use the sites framework in your Django views to do
  108
+You can use the sites framework in your Django views to do
109 109
 particular things based on the site in which the view is being called.
110 110
 For example::
111 111
 
@@ -146,6 +146,16 @@ the :class:`~django.contrib.sites.models.Site` model's manager has a
146 146
         else:
147 147
             # Do something else.
148 148
 
  149
+.. versionchanged:: 1.3
  150
+
  151
+For code which relies on getting the current domain but cannot be certain
  152
+that the sites framework will be installed for any given project, there is a
  153
+utility function :func:`~django.contrib.sites.models.get_current_site` that
  154
+takes a request object as an argument and returns either a Site instance (if
  155
+the sites framework is installed) or a RequestSite instance (if it is not).
  156
+This allows loose coupling with the sites framework and provides a usable
  157
+fallback for cases where it is not installed.
  158
+
149 159
 Getting the current domain for display
150 160
 --------------------------------------
151 161
 

0 notes on commit 12187bf

Please sign in to comment.
Something went wrong with that request. Please try again.