Ticket #18148: sites.diff

File sites.diff, 7.2 KB (added by Rory Geoghegan, 12 years ago)

Patch for django-trunk revision 17913

  • docs/ref/contrib/sites.txt

     
    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
     352.. versionchanged:: 1.5
     353
     354:class:`~django.contrib.sites.managers.CurrentSiteManager` can span
     355multiple models by using the same syntax as queries, as per the
     356:ref:`models and database queries documentation<field-lookups-intro>`. For
     357example, using the ``Photo`` model defined above::
     358
     359    from django.db import models
     360    from django.contrib.sites.managers import CurrentSiteManager
     361
     362    class PhotoLocation(models.Model):
     363        name = models.CharField(max_length=100)
     364        photo = models.ForeignKey(Photo)
     365        objects = models.Manager()
     366        on_site = CurrentSiteManager('photo__publish_on')
     367
     368``PhotoLocation.on_site.all()`` will return all ``PhotoLocation`` objects
     369in the database associated with ``Photo`` objects which themselves are
     370associated with the current site.
     371
    352372Finally, note that you'll probably want to keep a normal
    353373(non-site-specific) ``Manager`` on your model, even if you use
    354374:class:`~django.contrib.sites.managers.CurrentSiteManager`. As
  • django/contrib/sites/managers.py

     
    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."
     
    1011        self.__is_validated = False
    1112       
    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:
     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' as
     23        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()
    2228            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 'site' or 'sites' in %s." %
     35                    (self.__class__.__name__, self.model._meta.object_name)
     36                )
     37
     38        fieldname_chain = self.__field_name.split(constants.LOOKUP_SEP)
     39        model = self.model
     40
     41        for fieldname in fieldname_chain:
     42            # Throws an exception if anything goes bad
     43            self._validate_single_field_name(model, fieldname)
     44            model = self._get_related_model(model, fieldname)
     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)
     55            field = model._meta.get_field(field_name)
    3156            if not isinstance(field, (models.ForeignKey, models.ManyToManyField)):
    32                 raise TypeError("%s must be a ForeignKey or ManyToManyField." %self.__field_name)
     57                raise TypeError("%s must be a ForeignKey or ManyToManyField." % field_name)
    3358        except FieldDoesNotExist:
    3459            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
     60                    (self.__class__.__name__, self.__field_name, model._meta.object_name))
    3761   
     62    def _get_related_model(self, model, fieldname):
     63        """
     64        Given a model and the name of a ForeignKey or ManyToManyField field
     65        name as a string, returns the associated model.
     66        """
     67        return model._meta.get_field_by_name(fieldname)[0].rel.to
     68
    3869    def get_query_set(self):
    3970        if not self.__is_validated:
    4071            self._validate_field_name()
  • tests/regressiontests/sites_framework/tests.py

     
    55from django.test import TestCase
    66
    77from .models import (SyndicatedArticle, ExclusiveArticle, CustomArticle,
    8     InvalidArticle, ConfusedArticle)
     8    InvalidArticle, ConfusedArticle, ArticleComment)
    99
    1010
    1111class 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 = ExclusiveArticle.objects.create(
     42            title="Breaking News!",
     43            site_id=settings.SITE_ID
     44        )
     45        article2 = ExclusiveArticle.objects.create(
     46            title="More News!",
     47            site_id=settings.SITE_ID + 1
     48        )
     49
     50        comment = ArticleComment.objects.create(
     51            parent_article=article,
     52            text="First post!"
     53        )
     54        comment2 = ArticleComment.objects.create(
     55            parent_article=article2,
     56            text="Second post."
     57        )
     58
     59        self.assertEqual(
     60            ArticleComment.on_site.all().get(),
     61            comment
     62        )
  • tests/regressiontests/sites_framework/models.py

     
    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")
Back to Top