Ticket #5768: 5768-m2m-values-r14618.diff
File 5768-m2m-values-r14618.diff, 9.6 KB (added by , 14 years ago) |
---|
-
django/db/models/query.py
490 490 # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS # 491 491 ################################################## 492 492 493 def values(self, *fields): 494 return self._clone(klass=ValuesQuerySet, setup=True, _fields=fields) 493 def values(self, *fields, **kwargs): 494 allow_m2m = kwargs.pop('allow_m2m', False) 495 if kwargs: 496 raise TypeError('Unexpected keyword arguments to values_list: %s' 497 % (kwargs.keys(),)) 498 return self._clone(klass=ValuesQuerySet, setup=True, _fields=fields, 499 allow_m2m=allow_m2m) 495 500 496 501 def values_list(self, *fields, **kwargs): 497 502 flat = kwargs.pop('flat', False) 503 allow_m2m = kwargs.pop('allow_m2m', False) 498 504 if kwargs: 499 505 raise TypeError('Unexpected keyword arguments to values_list: %s' 500 506 % (kwargs.keys(),)) 501 507 if flat and len(fields) > 1: 502 508 raise TypeError("'flat' is not valid when values_list is called with more than one field.") 503 509 return self._clone(klass=ValuesListQuerySet, setup=True, flat=flat, 504 _fields=fields )510 _fields=fields, allow_m2m=allow_m2m) 505 511 506 512 def dates(self, field_name, kind, order='ASC'): 507 513 """ … … 870 876 self.query.select = [] 871 877 if self.extra_names is not None: 872 878 self.query.set_extra_mask(self.extra_names) 873 self.query.add_fields(self.field_names, False)879 self.query.add_fields(self.field_names, self.allow_m2m) 874 880 if self.aggregate_names is not None: 875 881 self.query.set_aggregate_mask(self.aggregate_names) 876 882 … … 886 892 c.field_names = self.field_names 887 893 c.extra_names = self.extra_names 888 894 c.aggregate_names = self.aggregate_names 895 c.allow_m2m = self.allow_m2m 889 896 if setup and hasattr(c, '_setup_query'): 890 897 c._setup_query() 891 898 return c … … 972 979 def _clone(self, *args, **kwargs): 973 980 clone = super(ValuesListQuerySet, self)._clone(*args, **kwargs) 974 981 clone.flat = self.flat 982 clone.allow_m2m = self.allow_m2m 975 983 return clone 976 984 977 985 -
tests/modeltests/lookup/tests.py
3 3 from django.core.exceptions import FieldError 4 4 from django.db import connection 5 5 from django.test import TestCase, skipUnlessDBFeature 6 from models import A rticle6 from models import Author, Article, Tag 7 7 8 8 9 9 class LookupTests(TestCase): 10 10 11 11 #def setUp(self): 12 12 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() 13 18 # 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) 15 20 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) 17 22 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) 19 24 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) 21 26 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) 23 28 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) 25 30 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) 27 32 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) 28 43 29 44 def test_exists(self): 30 45 # We can use .exists() to check that there are some … … 188 203 self.assertRaises(FieldError, 189 204 Article.objects.extra(select={'id_plus_one': 'id + 1'}).values, 190 205 'id', 'id_plus_two') 206 # You can specify fields from related models that cross a single-valued 207 # relation (one-to-one, many-to-one). 208 self.assertQuerysetEqual( 209 Article.objects.values('headline', 'author__name'), 210 [ 211 {'headline': self.a5.headline, 'author__name': self.au2.name}, 212 {'headline': self.a6.headline, 'author__name': self.au2.name}, 213 {'headline': self.a4.headline, 'author__name': self.au1.name}, 214 {'headline': self.a2.headline, 'author__name': self.au1.name}, 215 {'headline': self.a3.headline, 'author__name': self.au1.name}, 216 {'headline': self.a7.headline, 'author__name': self.au2.name}, 217 {'headline': self.a1.headline, 'author__name': self.au1.name}, 218 ], transform=identity) 219 # If you want to specify fields from related models that cross a 220 # multi-valued relation, you need to use the allow_m2m argument. 221 self.assertQuerysetEqual( 222 Author.objects.values('name', 'article__headline', allow_m2m=True), 223 [ 224 {'name': self.au1.name, 'article__headline': self.a1.headline}, 225 {'name': self.au1.name, 'article__headline': self.a2.headline}, 226 {'name': self.au1.name, 'article__headline': self.a3.headline}, 227 {'name': self.au1.name, 'article__headline': self.a4.headline}, 228 {'name': self.au2.name, 'article__headline': self.a5.headline}, 229 {'name': self.au2.name, 'article__headline': self.a6.headline}, 230 {'name': self.au2.name, 'article__headline': self.a7.headline}, 231 ], transform=identity) 232 self.assertQuerysetEqual( 233 Author.objects.values('name', 'article__headline', 'article__tag__name', allow_m2m=True), 234 [ 235 {'name': self.au1.name, 'article__headline': self.a1.headline, 'article__tag__name': self.t1.name}, 236 {'name': self.au1.name, 'article__headline': self.a2.headline, 'article__tag__name': self.t1.name}, 237 {'name': self.au1.name, 'article__headline': self.a3.headline, 'article__tag__name': self.t1.name}, 238 {'name': self.au1.name, 'article__headline': self.a3.headline, 'article__tag__name': self.t2.name}, 239 {'name': self.au1.name, 'article__headline': self.a4.headline, 'article__tag__name': self.t2.name}, 240 {'name': self.au2.name, 'article__headline': self.a5.headline, 'article__tag__name': self.t2.name}, 241 {'name': self.au2.name, 'article__headline': self.a5.headline, 'article__tag__name': self.t3.name}, 242 {'name': self.au2.name, 'article__headline': self.a6.headline, 'article__tag__name': self.t3.name}, 243 {'name': self.au2.name, 'article__headline': self.a7.headline, 'article__tag__name': self.t3.name}, 244 ], transform=identity) 191 245 # If you don't specify field names to values(), all are returned. 192 246 self.assertQuerysetEqual(Article.objects.filter(id=self.a5.id).values(), 193 247 [{ 194 248 'id': self.a5.id, 249 'author_id': self.a2.id, 195 250 'headline': 'Article 5', 196 251 'pub_date': datetime(2005, 8, 1, 9, 0) 197 252 }], transform=identity) … … 402 457 self.fail('FieldError not raised') 403 458 except FieldError, ex: 404 459 self.assertEqual(str(ex), "Cannot resolve keyword 'pub_date_year' " 405 "into field. Choices are: headline, id, pub_date")460 "into field. Choices are: author, headline, id, pub_date, tag") 406 461 try: 407 462 Article.objects.filter(headline__starts='Article') 408 463 self.fail('FieldError not raised') -
tests/modeltests/lookup/models.py
7 7 from django.db import models, DEFAULT_DB_ALIAS, connection 8 8 from django.conf import settings 9 9 10 class Author(models.Model): 11 name = models.CharField(max_length=100) 12 10 13 class Article(models.Model): 11 14 headline = models.CharField(max_length=100) 12 15 pub_date = models.DateTimeField() 16 author = models.ForeignKey(Author, blank=True, null=True) 13 17 class Meta: 14 18 ordering = ('-pub_date', 'headline') 15 19 16 20 def __unicode__(self): 17 21 return self.headline 22 23 class Tag(models.Model): 24 articles = models.ManyToManyField(Article) 25 name = models.CharField(max_length=100)