Ticket #18148: 18148_v3.diff

File 18148_v3.diff, 8.7 KB (added by Christopher Medrela, 12 years ago)
  • django/contrib/sites/managers.py

    diff --git a/django/contrib/sites/managers.py b/django/contrib/sites/managers.py
    index 3df485a..58896ee 100644
    a b  
    11from django.conf import settings
    22from django.db import models
    33from django.db.models.fields import FieldDoesNotExist
     4from django.db.models.sql import constants
    45
    56class CurrentSiteManager(models.Manager):
    67    "Use this to limit objects to those associated with the current site."
    class CurrentSiteManager(models.Manager):  
    89        super(CurrentSiteManager, self).__init__()
    910        self.__field_name = field_name
    1011        self.__is_validated = False
    11        
     12
    1213    def _validate_field_name(self):
    13         field_names = self.model._meta.get_all_field_names()
    14        
    15         # If a custom name is provided, make sure the field exists on the model
    16         if self.__field_name is not None and self.__field_name not in field_names:
    17             raise ValueError("%s couldn't find a field named %s in %s." % \
    18                 (self.__class__.__name__, self.__field_name, self.model._meta.object_name))
    19        
    20         # Otherwise, see if there is a field called either 'site' or 'sites'
    21         else:
    22             for potential_name in ['site', 'sites']:
     14        """
     15        Given the field identifier, goes down the chain to check that each
     16        specified field
     17
     18            a) exists,
     19            b) is of type ForeignKey or ManyToManyField
     20
     21        If no field name is specified when instantiating
     22        CurrentSiteManager, it tries to find either 'site' or 'sites'
     23        as the site link.
     24        """
     25        if self.__field_name is None:
     26            # Guess at field name
     27            field_names = self.model._meta.get_all_field_names()
     28            for potential_name in ('site', 'sites'):
    2329                if potential_name in field_names:
    2430                    self.__field_name = potential_name
    25                     self.__is_validated = True
    2631                    break
    27        
    28         # Now do a type check on the field (FK or M2M only)
     32            else:
     33                raise ValueError(
     34                    "%s couldn't find a field named either "
     35                    "'site' or 'sites' in %s." %
     36                    (self.__class__.__name__, self.model._meta.object_name))
     37
     38        fieldname_chain = self.__field_name.split(constants.LOOKUP_SEP)
     39        model = self.model
     40
     41        for field_name in fieldname_chain:
     42            # Throws an exception if anything goes bad
     43            self._validate_single_field_name(model, field_name)
     44            model = self._get_related_model(model, field_name)
     45
     46        # If we get this far without an exception, everything is good
     47        self.__is_validated = True
     48
     49    def _validate_single_field_name(self, model, field_name):
     50        """
     51        Checks if the given field name can be used to make a link between a
     52        model and a site with the CurrentSiteManager class
     53        """
    2954        try:
    30             field = self.model._meta.get_field(self.__field_name)
    31             if not isinstance(field, (models.ForeignKey, models.ManyToManyField)):
    32                 raise TypeError("%s must be a ForeignKey or ManyToManyField." %self.__field_name)
     55            field = model._meta.get_field(field_name)
     56            if not isinstance(field, (models.ForeignKey,
     57                                      models.ManyToManyField)):
     58                raise TypeError(
     59                    "Field %s of model %s must be a ForeignKey "
     60                    "or ManyToManyField." %
     61                    (field_name, model._meta.object_name))
    3362        except FieldDoesNotExist:
    34             raise ValueError("%s couldn't find a field named %s in %s." % \
    35                     (self.__class__.__name__, self.__field_name, self.model._meta.object_name))
    36         self.__is_validated = True
    37    
     63            raise ValueError(
     64                "%s couldn't find a field named %s in %s." %
     65                (self.__class__.__name__, field_name, model._meta.object_name))
     66
     67    def _get_related_model(self, model, fieldname):
     68        """
     69        Given a model and the name of a ForeignKey or ManyToManyField field
     70        name as a string, returns the associated model.
     71        """
     72        return model._meta.get_field_by_name(fieldname)[0].rel.to
     73
    3874    def get_query_set(self):
    3975        if not self.__is_validated:
    4076            self._validate_field_name()
  • docs/ref/contrib/sites.txt

    diff --git a/docs/ref/contrib/sites.txt b/docs/ref/contrib/sites.txt
    index 8fc434b..7cf5106 100644
    a b fallback for cases where it is not installed.  
    174174.. function:: get_current_site(request)
    175175
    176176    Checks if contrib.sites is installed and returns either the current
    177     :class:`~django.contrib.sites.models.Site` object or a 
     177    :class:`~django.contrib.sites.models.Site` object or a
    178178    :class:`~django.contrib.sites.models.RequestSite` object based on
    179179    the request.
    180180
    demonstrates this::  
    349349If you attempt to use :class:`~django.contrib.sites.managers.CurrentSiteManager`
    350350and pass a field name that doesn't exist, Django will raise a ``ValueError``.
    351351
     352Spanning relationships
     353----------------------
     354
     355.. versionchanged:: 1.5
     356
     357:class:`~django.contrib.sites.managers.CurrentSiteManager` can span
     358multiple models by using the same syntax as queries, as per the
     359:ref:`models and database queries documentation
     360<lookups-that-span-relationships>`. For example, using the ``Photo``
     361model defined above::
     362
     363    from django.db import models
     364    from django.contrib.sites.managers import CurrentSiteManager
     365
     366    class PhotoLocation(models.Model):
     367        name = models.CharField(max_length=100)
     368        photo = models.ForeignKey(Photo)
     369        objects = models.Manager()
     370        on_site = CurrentSiteManager('photo__publish_on')
     371
     372``PhotoLocation.on_site.all()`` will return all ``PhotoLocation`` objects
     373in the database associated with ``Photo`` objects which themselves are
     374associated with the current site.
     375
     376Keeping default manager
     377-----------------------
     378
    352379Finally, note that you'll probably want to keep a normal
    353380(non-site-specific) ``Manager`` on your model, even if you use
    354381:class:`~django.contrib.sites.managers.CurrentSiteManager`. As
    fallback when the database-backed sites framework is not available.  
    437464
    438465        Sets the ``name`` and ``domain`` attributes to the value of
    439466        :meth:`~django.http.HttpRequest.get_host`.
    440        
     467
    441468
    442469A :class:`~django.contrib.sites.models.RequestSite` object has a similar
    443470interface to a normal :class:`~django.contrib.sites.models.Site` object, except
  • tests/regressiontests/sites_framework/models.py

    diff --git a/tests/regressiontests/sites_framework/models.py b/tests/regressiontests/sites_framework/models.py
    index 9ecc3e6..90d26f1 100644
    a b class InvalidArticle(AbstractArticle):  
    3434
    3535class ConfusedArticle(AbstractArticle):
    3636    site = models.IntegerField()
     37
     38class ArticleComment(models.Model):
     39    parent_article = models.ForeignKey(ExclusiveArticle)
     40    text = models.CharField(max_length=50)
     41
     42    objects = models.Manager()
     43    on_site = CurrentSiteManager("parent_article__site")
  • tests/regressiontests/sites_framework/tests.py

    diff --git a/tests/regressiontests/sites_framework/tests.py b/tests/regressiontests/sites_framework/tests.py
    index 8e664fd..73a07d0 100644
    a b from django.contrib.sites.models import Site  
    55from django.test import TestCase
    66
    77from .models import (SyndicatedArticle, ExclusiveArticle, CustomArticle,
    8     InvalidArticle, ConfusedArticle)
     8    InvalidArticle, ConfusedArticle, ArticleComment)
    99
    1010
    1111class SitesFrameworkTestCase(TestCase):
    class SitesFrameworkTestCase(TestCase):  
    3636    def test_invalid_field_type(self):
    3737        article = ConfusedArticle.objects.create(title="More Bad News!", site=settings.SITE_ID)
    3838        self.assertRaises(TypeError, ConfusedArticle.on_site.all)
     39
     40    def test_indirect_link(self):
     41        article_inside = ExclusiveArticle.objects.create(
     42            title="Article in current site",
     43            site_id=settings.SITE_ID)
     44        article_outside = ExclusiveArticle.objects.create(
     45            title="Article in another site",
     46            site_id=settings.SITE_ID + 1)
     47
     48        comment_inside = ArticleComment.objects.create(
     49            parent_article=article_inside,
     50            text="Post to article in current side.")
     51        comment_outside = ArticleComment.objects.create(
     52            parent_article=article_outside,
     53            text="Post to article in another site.")
     54
     55        self.assertEqual(ArticleComment.on_site.all().get(), comment_inside)
Back to Top