diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index fd0a295..8b02bff 100644
a
|
b
|
class Field(object):
|
283 | 283 | return value._prepare() |
284 | 284 | |
285 | 285 | if lookup_type in ( |
286 | | 'regex', 'iregex', 'month', 'day', 'week_day', 'search', |
287 | | 'contains', 'icontains', 'iexact', 'startswith', 'istartswith', |
| 286 | 'month', 'day', 'week_day', 'hour', 'minute', 'second', |
| 287 | 'regex', 'iregex', 'search', 'contains', 'icontains', |
| 288 | 'iexact', 'startswith', 'istartswith', |
288 | 289 | 'endswith', 'iendswith', 'isnull' |
289 | 290 | ): |
290 | 291 | return value |
… |
… |
class Field(object):
|
317 | 318 | sql, params = value._as_sql(connection=connection) |
318 | 319 | return QueryWrapper(('(%s)' % sql), params) |
319 | 320 | |
320 | | if lookup_type in ('regex', 'iregex', 'month', 'day', 'week_day', 'search'): |
| 321 | if lookup_type in ('regex', 'iregex', 'month', 'day', 'week_day', 'hour', 'minute', 'second', 'search'): |
321 | 322 | return [value] |
322 | 323 | elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'): |
323 | 324 | return [self.get_db_prep_value(value, connection=connection, prepared=prepared)] |
… |
… |
class DateField(Field):
|
639 | 640 | def get_prep_lookup(self, lookup_type, value): |
640 | 641 | # For "__month", "__day", and "__week_day" lookups, convert the value |
641 | 642 | # to an int so the database backend always sees a consistent type. |
642 | | if lookup_type in ('month', 'day', 'week_day'): |
| 643 | if lookup_type in ('month', 'day', 'week_day', 'hour', 'minute', 'second'): |
643 | 644 | return int(value) |
644 | 645 | return super(DateField, self).get_prep_lookup(lookup_type, value) |
645 | 646 | |
diff --git a/django/db/models/sql/constants.py b/django/db/models/sql/constants.py
index 63c704f..95b244d 100644
a
|
b
|
import re
|
4 | 4 | QUERY_TERMS = dict([(x, None) for x in ( |
5 | 5 | 'exact', 'iexact', 'contains', 'icontains', 'gt', 'gte', 'lt', 'lte', 'in', |
6 | 6 | 'startswith', 'istartswith', 'endswith', 'iendswith', 'range', 'year', |
7 | | 'month', 'day', 'week_day', 'isnull', 'search', 'regex', 'iregex', |
| 7 | 'month', 'day', 'week_day', 'hour', 'minute', 'second', |
| 8 | 'isnull', 'search', 'regex', 'iregex' |
8 | 9 | )]) |
9 | 10 | |
10 | 11 | # Size of each "chunk" for get_iterator calls. |
diff --git a/django/db/models/sql/where.py b/django/db/models/sql/where.py
index 2427a52..62481f6 100644
a
|
b
|
class WhereNode(tree.Node):
|
199 | 199 | params) |
200 | 200 | elif lookup_type in ('range', 'year'): |
201 | 201 | return ('%s BETWEEN %%s and %%s' % field_sql, params) |
202 | | elif lookup_type in ('month', 'day', 'week_day'): |
| 202 | elif lookup_type in ('month', 'day', 'week_day', 'hour', 'minute', 'second'): |
203 | 203 | return ('%s = %%s' % connection.ops.date_extract_sql(lookup_type, field_sql), |
204 | 204 | params) |
205 | 205 | elif lookup_type == 'isnull': |
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
index 8bff953..948fd5b 100644
a
|
b
|
Note this will match any record with a pub_date that falls on a Monday (day 2
|
1646 | 1646 | of the week), regardless of the month or year in which it occurs. Week days |
1647 | 1647 | are indexed with day 1 being Sunday and day 7 being Saturday. |
1648 | 1648 | |
| 1649 | .. fieldlookup:: hour |
| 1650 | |
| 1651 | hour |
| 1652 | ~~~~ |
| 1653 | |
| 1654 | .. versionadded:: 1.3 |
| 1655 | |
| 1656 | For date/datetime fields, exact hour match. |
| 1657 | |
| 1658 | Example:: |
| 1659 | |
| 1660 | Entry.objects.filter(pub_date__hour=3) |
| 1661 | |
| 1662 | SQL equivalent:: |
| 1663 | |
| 1664 | SELECT ... WHERE EXTRACT('hour' FROM pub_date) = '3'; |
| 1665 | |
| 1666 | (The exact SQL syntax varies for each database engine.) |
| 1667 | |
| 1668 | This will match every record with a pub_date that occurred between |
| 1669 | 3:00 AM and 3:59 AM. |
| 1670 | |
| 1671 | .. fieldlookup:: minute |
| 1672 | |
| 1673 | minute |
| 1674 | ~~~~~~ |
| 1675 | |
| 1676 | .. versionadded:: 1.3 |
| 1677 | |
| 1678 | For date/datetime fields, exact minute match. |
| 1679 | |
| 1680 | Example:: |
| 1681 | |
| 1682 | Entry.objects.filter(pub_date__minute=30) |
| 1683 | |
| 1684 | SQL equivalent:: |
| 1685 | |
| 1686 | SELECT ... WHERE EXTRACT('minute' FROM pub_date) = '30'; |
| 1687 | |
| 1688 | (The exact SQL syntax varies for each database engine.) |
| 1689 | |
| 1690 | This will match every record with a pub_date that occurred at the 30th |
| 1691 | minute of any hour, such as 8:30 AM or 11:30 PM. |
| 1692 | |
| 1693 | .. fieldlookup:: second |
| 1694 | |
| 1695 | second |
| 1696 | ~~~~~~ |
| 1697 | |
| 1698 | .. versionadded:: 1.3 |
| 1699 | |
| 1700 | For date/datetime fields, exact second match. |
| 1701 | |
| 1702 | Example:: |
| 1703 | |
| 1704 | Entry.objects.filter(pub_date__second=0) |
| 1705 | |
| 1706 | SQL equivalent:: |
| 1707 | |
| 1708 | SELECT ... WHERE EXTRACT('second' FROM pub_date) = '0'; |
| 1709 | |
| 1710 | (The exact SQL syntax varies for each database engine.) |
| 1711 | |
| 1712 | This will match every record with a pub_date that occurred at the 0th |
| 1713 | second of any minute. |
| 1714 | |
1649 | 1715 | .. fieldlookup:: isnull |
1650 | 1716 | |
1651 | 1717 | isnull |
diff --git a/tests/modeltests/basic/tests.py b/tests/modeltests/basic/tests.py
index 966798d..af8e965 100644
a
|
b
|
class ModelTest(TestCase):
|
18 | 18 | a = Article( |
19 | 19 | id=None, |
20 | 20 | headline='Area man programs in Python', |
21 | | pub_date=datetime(2005, 7, 28), |
| 21 | pub_date=datetime(2005, 7, 28, 9, 23, 34), |
22 | 22 | ) |
23 | 23 | |
24 | 24 | # Save it into the database. You have to call save() explicitly. |
… |
… |
class ModelTest(TestCase):
|
33 | 33 | |
34 | 34 | # Access database columns via Python attributes. |
35 | 35 | self.assertEqual(a.headline, 'Area man programs in Python') |
36 | | self.assertEqual(a.pub_date, datetime(2005, 7, 28, 0, 0)) |
| 36 | self.assertEqual(a.pub_date, datetime(2005, 7, 28, 9, 23, 34)) |
37 | 37 | |
38 | 38 | # Change values by changing the attributes, then calling save(). |
39 | 39 | a.headline = 'Area woman programs in Python' |
… |
… |
class ModelTest(TestCase):
|
50 | 50 | self.assertEqual(Article.objects.get(pub_date__year=2005, pub_date__month=7), a) |
51 | 51 | self.assertEqual(Article.objects.get(pub_date__year=2005, pub_date__month=7, pub_date__day=28), a) |
52 | 52 | self.assertEqual(Article.objects.get(pub_date__week_day=5), a) |
| 53 | self.assertEqual(Article.objects.get(pub_date__hour=9, pub_date__minute=23), a) |
| 54 | self.assertEqual(Article.objects.get(pub_date__hour=9), a) |
| 55 | self.assertEqual(Article.objects.get(pub_date__minute=23), a) |
| 56 | self.assertEqual(Article.objects.get(pub_date__second=34), a) |
53 | 57 | |
54 | 58 | # The "__exact" lookup type can be omitted, as a shortcut. |
55 | 59 | self.assertEqual(Article.objects.get(id=a.id), a) |
… |
… |
class ModelTest(TestCase):
|
76 | 80 | Article.objects.filter(pub_date__week_day=6), |
77 | 81 | [], |
78 | 82 | ) |
| 83 | self.assertQuerysetEqual( |
| 84 | Article.objects.filter(pub_date__hour=9), |
| 85 | ['<Article: Area woman programs in Python>'], |
| 86 | ) |
| 87 | self.assertQuerysetEqual( |
| 88 | Article.objects.filter(pub_date__minute=23), |
| 89 | ['<Article: Area woman programs in Python>'], |
| 90 | ) |
| 91 | self.assertQuerysetEqual( |
| 92 | Article.objects.filter(pub_date__month=7, pub_date__hour=8), |
| 93 | [], |
| 94 | ) |
| 95 | self.assertQuerysetEqual( |
| 96 | Article.objects.filter(pub_date__month=7, pub_date__second=27), |
| 97 | [], |
| 98 | ) |
79 | 99 | |
80 | 100 | # Django raises an Article.DoesNotExist exception for get() if the |
81 | 101 | # parameters don't match any object. |
… |
… |
class ModelTest(TestCase):
|
101 | 121 | pub_date__week_day=6, |
102 | 122 | ) |
103 | 123 | |
| 124 | self.assertRaisesRegexp( |
| 125 | ObjectDoesNotExist, |
| 126 | "Article matching query does not exist.", |
| 127 | Article.objects.get, |
| 128 | pub_date__second=30, |
| 129 | ) |
| 130 | |
104 | 131 | # Lookup by a primary key is the most common case, so Django |
105 | 132 | # provides a shortcut for primary-key exact lookups. |
106 | 133 | # The following is identical to articles.get(id=a.id). |
diff --git a/tests/regressiontests/null_queries/models.py b/tests/regressiontests/null_queries/models.py
index 442535c..adcb0bd 100644
a
|
b
|
class OuterB(models.Model):
|
22 | 22 | |
23 | 23 | class Inner(models.Model): |
24 | 24 | first = models.ForeignKey(OuterA) |
25 | | second = models.ForeignKey(OuterB, null=True) |
| 25 | secondary = models.ForeignKey(OuterB, null=True) |
diff --git a/tests/regressiontests/null_queries/tests.py b/tests/regressiontests/null_queries/tests.py
index 72dcd51..edc0cc6 100644
a
|
b
|
class NullQueriesTests(TestCase):
|
53 | 53 | """ |
54 | 54 | obj = OuterA.objects.create() |
55 | 55 | self.assertQuerysetEqual( |
56 | | OuterA.objects.filter(inner__second=None), |
| 56 | OuterA.objects.filter(inner__secondary=None), |
57 | 57 | ['<OuterA: OuterA object>'] |
58 | 58 | ) |
59 | 59 | self.assertQuerysetEqual( |
60 | | OuterA.objects.filter(inner__second__data=None), |
| 60 | OuterA.objects.filter(inner__secondary__data=None), |
61 | 61 | ['<OuterA: OuterA object>'] |
62 | 62 | ) |
63 | 63 | |
64 | 64 | inner_obj = Inner.objects.create(first=obj) |
65 | 65 | self.assertQuerysetEqual( |
66 | | Inner.objects.filter(first__inner__second=None), |
| 66 | Inner.objects.filter(first__inner__secondary=None), |
67 | 67 | ['<Inner: Inner object>'] |
68 | 68 | ) |
69 | 69 | |