Fixed #13935, added support for using QuerySet.dates across related fields. Thanks to valyagolev for his work on the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14461 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Alex Gaynor 2010-11-04 16:03:05 +00:00
parent 127506aeac
commit abaa3ed4bd
5 changed files with 121 additions and 10 deletions

View File

@ -7,7 +7,8 @@ from itertools import izip
from django.db import connections, router, transaction, IntegrityError from django.db import connections, router, transaction, IntegrityError
from django.db.models.aggregates import Aggregate from django.db.models.aggregates import Aggregate
from django.db.models.fields import DateField from django.db.models.fields import DateField
from django.db.models.query_utils import Q, select_related_descend, CollectedObjects, CyclicDependency, deferred_class_factory, InvalidQuery from django.db.models.query_utils import (Q, select_related_descend,
CollectedObjects, CyclicDependency, deferred_class_factory, InvalidQuery)
from django.db.models import signals, sql from django.db.models import signals, sql
from django.utils.copycompat import deepcopy from django.utils.copycompat import deepcopy
@ -998,12 +999,7 @@ class DateQuerySet(QuerySet):
self.query.clear_deferred_loading() self.query.clear_deferred_loading()
self.query = self.query.clone(klass=sql.DateQuery, setup=True) self.query = self.query.clone(klass=sql.DateQuery, setup=True)
self.query.select = [] self.query.select = []
field = self.model._meta.get_field(self._field_name, many_to_many=False) self.query.add_date_select(self._field_name, self._kind, self._order)
assert isinstance(field, DateField), "%r isn't a DateField." \
% field.name
self.query.add_date_select(field, self._kind, self._order)
if field.null:
self.query.add_filter(('%s__isnull' % field.name, False))
def _clone(self, klass=None, setup=False, **kwargs): def _clone(self, klass=None, setup=False, **kwargs):
c = super(DateQuerySet, self)._clone(klass, False, **kwargs) c = super(DateQuerySet, self)._clone(klass, False, **kwargs)

View File

@ -4,6 +4,7 @@ Query subclasses which provide extra functionality beyond simple data retrieval.
from django.core.exceptions import FieldError from django.core.exceptions import FieldError
from django.db import connections from django.db import connections
from django.db.models.fields import DateField
from django.db.models.sql.constants import * from django.db.models.sql.constants import *
from django.db.models.sql.datastructures import Date from django.db.models.sql.datastructures import Date
from django.db.models.sql.expressions import SQLEvaluator from django.db.models.sql.expressions import SQLEvaluator
@ -184,12 +185,19 @@ class DateQuery(Query):
compiler = 'SQLDateCompiler' compiler = 'SQLDateCompiler'
def add_date_select(self, field, lookup_type, order='ASC'): def add_date_select(self, field_name, lookup_type, order='ASC'):
""" """
Converts the query into a date extraction query. Converts the query into a date extraction query.
""" """
result = self.setup_joins([field.name], self.get_meta(), result = self.setup_joins(
self.get_initial_alias(), False) field_name.split(LOOKUP_SEP),
self.get_meta(),
self.get_initial_alias(),
False
)
field = result[0]
assert isinstance(field, DateField), "%r isn't a DateField." \
% field.name
alias = result[3][-1] alias = result[3][-1]
select = Date((alias, field.column), lookup_type) select = Date((alias, field.column), lookup_type)
self.select = [select] self.select = [select]
@ -199,6 +207,9 @@ class DateQuery(Query):
self.distinct = True self.distinct = True
self.order_by = order == 'ASC' and [1] or [-1] self.order_by = order == 'ASC' and [1] or [-1]
if field.null:
self.add_filter(("%s__isnull" % field_name, False))
class AggregateQuery(Query): class AggregateQuery(Query):
""" """
An AggregateQuery takes another query as a parameter to the FROM An AggregateQuery takes another query as a parameter to the FROM

View File

View File

@ -0,0 +1,23 @@
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=100)
pub_date = models.DateField()
categories = models.ManyToManyField("Category", related_name="articles")
def __unicode__(self):
return self.title
class Comment(models.Model):
article = models.ForeignKey(Article, related_name="comments")
text = models.TextField()
pub_date = models.DateField()
approval_date = models.DateField(null=True)
def __unicode__(self):
return 'Comment to %s (%s)' % (self.article.title, self.pub_date)
class Category(models.Model):
name = models.CharField(max_length=255)

View File

@ -0,0 +1,81 @@
from datetime import datetime
from django.test import TestCase
from models import Article, Comment, Category
class DatesTests(TestCase):
def test_related_model_traverse(self):
a1 = Article.objects.create(
title="First one",
pub_date=datetime(2005, 7, 28),
)
a2 = Article.objects.create(
title="Another one",
pub_date=datetime(2010, 7, 28),
)
a3 = Article.objects.create(
title="Third one, in the first day",
pub_date=datetime(2005, 7, 28),
)
a1.comments.create(
text="Im the HULK!",
pub_date=datetime(2005, 7, 28),
)
a1.comments.create(
text="HULK SMASH!",
pub_date=datetime(2005, 7, 29),
)
a2.comments.create(
text="LMAO",
pub_date=datetime(2010, 7, 28),
)
a3.comments.create(
text="+1",
pub_date=datetime(2005, 8, 29),
)
c = Category.objects.create(name="serious-news")
c.articles.add(a1, a3)
self.assertQuerysetEqual(
Comment.objects.dates("article__pub_date", "year"), [
datetime(2005, 1, 1),
datetime(2010, 1, 1),
],
lambda d: d,
)
self.assertQuerysetEqual(
Comment.objects.dates("article__pub_date", "month"), [
datetime(2005, 7, 1),
datetime(2010, 7, 1),
],
lambda d: d
)
self.assertQuerysetEqual(
Comment.objects.dates("article__pub_date", "day"), [
datetime(2005, 7, 28),
datetime(2010, 7, 28),
],
lambda d: d
)
self.assertQuerysetEqual(
Article.objects.dates("comments__pub_date", "day"), [
datetime(2005, 7, 28),
datetime(2005, 7, 29),
datetime(2005, 8, 29),
datetime(2010, 7, 28),
],
lambda d: d
)
self.assertQuerysetEqual(
Article.objects.dates("comments__approval_date", "day"), []
)
self.assertQuerysetEqual(
Category.objects.dates("articles__pub_date", "day"), [
datetime(2005, 7, 28),
],
lambda d: d,
)