Ticket #5768: 5768-m2m-values-r14639.diff

File 5768-m2m-values-r14639.diff, 10.6 KB (added by Tai Lee, 13 years ago)
  • django/db/models/query.py

     
    870870        self.query.select = []
    871871        if self.extra_names is not None:
    872872            self.query.set_extra_mask(self.extra_names)
    873         self.query.add_fields(self.field_names, False)
     873        self.query.add_fields(self.field_names, True)
    874874        if self.aggregate_names is not None:
    875875            self.query.set_aggregate_mask(self.aggregate_names)
    876876
  • tests/modeltests/lookup/tests.py

     
    33from django.core.exceptions import FieldError
    44from django.db import connection
    55from django.test import TestCase, skipUnlessDBFeature
    6 from models import Article
     6from models import Author, Article, Tag
    77
    88
    99class LookupTests(TestCase):
    1010
    1111    #def setUp(self):
    1212    def setUp(self):
     13        # Create a few Authors.
     14        self.au1 = Author(name='Author 1')
     15        self.au1.save()
     16        self.au2 = Author(name='Author 2')
     17        self.au2.save()
    1318        # Create a couple of Articles.
    14         self.a1 = Article(headline='Article 1', pub_date=datetime(2005, 7, 26))
     19        self.a1 = Article(headline='Article 1', pub_date=datetime(2005, 7, 26), author=self.au1)
    1520        self.a1.save()
    16         self.a2 = Article(headline='Article 2', pub_date=datetime(2005, 7, 27))
     21        self.a2 = Article(headline='Article 2', pub_date=datetime(2005, 7, 27), author=self.au1)
    1722        self.a2.save()
    18         self.a3 = Article(headline='Article 3', pub_date=datetime(2005, 7, 27))
     23        self.a3 = Article(headline='Article 3', pub_date=datetime(2005, 7, 27), author=self.au1)
    1924        self.a3.save()
    20         self.a4 = Article(headline='Article 4', pub_date=datetime(2005, 7, 28))
     25        self.a4 = Article(headline='Article 4', pub_date=datetime(2005, 7, 28), author=self.au1)
    2126        self.a4.save()
    22         self.a5 = Article(headline='Article 5', pub_date=datetime(2005, 8, 1, 9, 0))
     27        self.a5 = Article(headline='Article 5', pub_date=datetime(2005, 8, 1, 9, 0), author=self.au2)
    2328        self.a5.save()
    24         self.a6 = Article(headline='Article 6', pub_date=datetime(2005, 8, 1, 8, 0))
     29        self.a6 = Article(headline='Article 6', pub_date=datetime(2005, 8, 1, 8, 0), author=self.au2)
    2530        self.a6.save()
    26         self.a7 = Article(headline='Article 7', pub_date=datetime(2005, 7, 27))
     31        self.a7 = Article(headline='Article 7', pub_date=datetime(2005, 7, 27), author=self.au2)
    2732        self.a7.save()
     33        # Create a few Tags.
     34        self.t1 = Tag(name='Tag 1')
     35        self.t1.save()
     36        self.t1.articles.add(self.a1, self.a2, self.a3)
     37        self.t2 = Tag(name='Tag 2')
     38        self.t2.save()
     39        self.t2.articles.add(self.a3, self.a4, self.a5)
     40        self.t3 = Tag(name='Tag 3')
     41        self.t3.save()
     42        self.t3.articles.add(self.a5, self.a6, self.a7)
    2843
    2944    def test_exists(self):
    3045        # We can use .exists() to check that there are some
     
    182197                'id_plus_seven': self.a1.id + 7,
    183198                'id_plus_eight': self.a1.id + 8,
    184199            }], transform=identity)
     200        # You can specify fields from forward and reverse relations, just like filter().
     201        self.assertQuerysetEqual(
     202            Article.objects.values('headline', 'author__name'),
     203            [
     204                {'headline': self.a5.headline, 'author__name': self.au2.name},
     205                {'headline': self.a6.headline, 'author__name': self.au2.name},
     206                {'headline': self.a4.headline, 'author__name': self.au1.name},
     207                {'headline': self.a2.headline, 'author__name': self.au1.name},
     208                {'headline': self.a3.headline, 'author__name': self.au1.name},
     209                {'headline': self.a7.headline, 'author__name': self.au2.name},
     210                {'headline': self.a1.headline, 'author__name': self.au1.name},
     211            ], transform=identity)
     212        self.assertQuerysetEqual(
     213            Author.objects.values('name', 'article__headline').order_by('name', 'article__headline'),
     214            [
     215                {'name': self.au1.name, 'article__headline': self.a1.headline},
     216                {'name': self.au1.name, 'article__headline': self.a2.headline},
     217                {'name': self.au1.name, 'article__headline': self.a3.headline},
     218                {'name': self.au1.name, 'article__headline': self.a4.headline},
     219                {'name': self.au2.name, 'article__headline': self.a5.headline},
     220                {'name': self.au2.name, 'article__headline': self.a6.headline},
     221                {'name': self.au2.name, 'article__headline': self.a7.headline},
     222            ], transform=identity)
     223        self.assertQuerysetEqual(
     224            Author.objects.values('name', 'article__headline', 'article__tag__name').order_by('name', 'article__headline', 'article__tag__name'),
     225            [
     226                {'name': self.au1.name, 'article__headline': self.a1.headline, 'article__tag__name': self.t1.name},
     227                {'name': self.au1.name, 'article__headline': self.a2.headline, 'article__tag__name': self.t1.name},
     228                {'name': self.au1.name, 'article__headline': self.a3.headline, 'article__tag__name': self.t1.name},
     229                {'name': self.au1.name, 'article__headline': self.a3.headline, 'article__tag__name': self.t2.name},
     230                {'name': self.au1.name, 'article__headline': self.a4.headline, 'article__tag__name': self.t2.name},
     231                {'name': self.au2.name, 'article__headline': self.a5.headline, 'article__tag__name': self.t2.name},
     232                {'name': self.au2.name, 'article__headline': self.a5.headline, 'article__tag__name': self.t3.name},
     233                {'name': self.au2.name, 'article__headline': self.a6.headline, 'article__tag__name': self.t3.name},
     234                {'name': self.au2.name, 'article__headline': self.a7.headline, 'article__tag__name': self.t3.name},
     235            ], transform=identity)
    185236        # However, an exception FieldDoesNotExist will be thrown if you specify
    186237        # a non-existent field name in values() (a field that is neither in the
    187238        # model nor in extra(select)).
     
    192243        self.assertQuerysetEqual(Article.objects.filter(id=self.a5.id).values(),
    193244            [{
    194245                'id': self.a5.id,
     246                'author_id': self.au2.id,
    195247                'headline': 'Article 5',
    196248                'pub_date': datetime(2005, 8, 1, 9, 0)
    197249            }], transform=identity)
     
    250302                (self.a7.id, self.a7.id+1)
    251303            ],
    252304            transform=identity)
     305        self.assertQuerysetEqual(
     306            Author.objects.values_list('name', 'article__headline', 'article__tag__name').order_by('name', 'article__headline', 'article__tag__name'),
     307            [
     308                (self.au1.name, self.a1.headline, self.t1.name),
     309                (self.au1.name, self.a2.headline, self.t1.name),
     310                (self.au1.name, self.a3.headline, self.t1.name),
     311                (self.au1.name, self.a3.headline, self.t2.name),
     312                (self.au1.name, self.a4.headline, self.t2.name),
     313                (self.au2.name, self.a5.headline, self.t2.name),
     314                (self.au2.name, self.a5.headline, self.t3.name),
     315                (self.au2.name, self.a6.headline, self.t3.name),
     316                (self.au2.name, self.a7.headline, self.t3.name),
     317            ], transform=identity)
    253318        self.assertRaises(TypeError, Article.objects.values_list, 'id', 'headline', flat=True)
    254319
    255320    def test_get_next_previous_by(self):
     
    402467            self.fail('FieldError not raised')
    403468        except FieldError, ex:
    404469            self.assertEqual(str(ex), "Cannot resolve keyword 'pub_date_year' "
    405                              "into field. Choices are: headline, id, pub_date")
     470                             "into field. Choices are: author, headline, id, pub_date, tag")
    406471        try:
    407472            Article.objects.filter(headline__starts='Article')
    408473            self.fail('FieldError not raised')
  • tests/modeltests/lookup/models.py

     
    77from django.db import models, DEFAULT_DB_ALIAS, connection
    88from django.conf import settings
    99
     10class Author(models.Model):
     11    name = models.CharField(max_length=100)
     12    class Meta:
     13        ordering = ('name', )
     14
    1015class Article(models.Model):
    1116    headline = models.CharField(max_length=100)
    1217    pub_date = models.DateTimeField()
     18    author = models.ForeignKey(Author, blank=True, null=True)
    1319    class Meta:
    1420        ordering = ('-pub_date', 'headline')
    1521
    1622    def __unicode__(self):
    1723        return self.headline
     24
     25class Tag(models.Model):
     26    articles = models.ManyToManyField(Article)
     27    name = models.CharField(max_length=100)
     28    class Meta:
     29        ordering = ('name', )
  • docs/ref/models/querysets.txt

     
    400400
    401401A couple of subtleties that are worth mentioning:
    402402
    403     * The ``values()`` method does not return anything for
    404       :class:`~django.db.models.ManyToManyField` attributes and will raise an
    405       error if you try to pass in this type of field to it.
    406403    * If you have a field called ``foo`` that is a
    407404      :class:`~django.db.models.ForeignKey`, the default ``values()`` call
    408405      will return a dictionary key called ``foo_id``, since this is the name
     
    453450but it doesn't really matter. This is your chance to really flaunt your
    454451individualism.
    455452
     453.. versionchanged:: 1.3
     454
     455The ``values()`` method previously did not return anything for
     456:class:`~django.db.models.ManyToManyField` attributes and would raise an error
     457if you tried to pass in this type of field to it.
     458
     459This restriction has been lifted, and you can now also refer to fields on
     460related models with reverse relations through ``OneToOneField``, ``ForeignKey``
     461and ``ManyToManyField`` attributes::
     462
     463        Blog.objects.values('name', 'entry__headline')
     464        [{'name': 'My blog', 'entry__headline': 'An entry'}, {'name': 'My blog', 'entry__headline': 'Another entry'}, ...]
     465
     466Please note that when using ``values()`` in this way it is possible to trigger
     467an exponential increase in the size of your result set when used with
     468``ManyToManyFields`` and large data sets.
     469
    456470``values_list(*fields)``
    457471~~~~~~~~~~~~~~~~~~~~~~~~
    458472
Back to Top