Ticket #10154: dateexpressions.diff
File dateexpressions.diff, 6.3 KB (added by , 16 years ago) |
---|
-
django/db/models/expressions.py
1 1 from copy import deepcopy 2 from datetime import datetime 2 import datetime 3 from django.db import connection 3 4 4 5 from django.utils import tree 5 6 … … 26 27 super(ExpressionNode, self).__init__(children, connector, negated) 27 28 28 29 def _combine(self, other, connector, reversed, node=None): 30 if isinstance(other, datetime.timedelta): 31 return DateModifierNode([self, other], connector) 32 29 33 if reversed: 30 34 obj = ExpressionNode([other], connector) 31 35 obj.add(node or self, connector) … … 108 112 109 113 def evaluate(self, evaluator, qn): 110 114 return evaluator.evaluate_leaf(self, qn) 115 116 class DateModifierNode(ExpressionNode): 117 """ 118 Node that implements the following syntax: 119 filter(end_date__gt=F('start_date') + datetime.timedelta(days=3, seconds=200)) 120 121 which translates into: 122 SQLITE: 123 where end_date > date(start_date, "3 days 200 seconds") 124 125 POSTGRES: 126 where end_date > (start_date + interval '3 days 200 seconds') 127 128 """ 129 def __init__(self, children, connector=None, negated=False): 130 if len(children) != 2: 131 raise TypeError('You have to specify a node and a timedelta.') 132 if connector is None: 133 raise TypeError('You have to specify a connector.') 134 super(DateModifierNode, self).__init__([children[0], DateModifierLeaf(children[1])], connector, negated) 135 136 def prepare(self, evaluator, query, allow_joins): 137 return evaluator.prepare_node(self, query, allow_joins) 138 139 def evaluate(self, evaluator, qn): 140 if not qn: 141 qn = connection.ops.quote_name 142 143 modifiers = self.children.pop().evaluate(evaluator, qn) 144 sql, params = evaluator.evaluate_node(self, qn) 145 146 return connection.ops.date_interval_sql(sql, self.connector, modifiers), params 147 148 class DateModifierLeaf(object): 149 def __init__(self, timedelta): 150 self.days = timedelta.days 151 self.seconds = timedelta.seconds 152 153 def prepare(self, evaluator, query, allow_joins): 154 pass 155 156 def evaluate(self, evaluator, qn): 157 # move exact representation to backend ? 158 mods = [] 159 if self.days: 160 mods.append(u'%s days' % self.days) 161 if self.seconds: 162 mods.append(u'%s seconds' % self.seconds) 163 return mods -
django/db/backends/postgresql/operations.py
28 28 # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT 29 29 return "EXTRACT('%s' FROM %s)" % (lookup_type, field_name) 30 30 31 def date_interval_sql(self, sql, connector, modifiers): 32 # implements the interval functionality for expressions 33 mods = u' '.join([u'%s' % modifier for modifier in modifiers]) 34 conn = u' %s ' % connector 35 return u'(%s)' % conn.join([sql, u'interval \'%s\'' % mods]) 36 31 37 def date_trunc_sql(self, lookup_type, field_name): 32 38 # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC 33 39 return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) -
django/db/backends/__init__.py
104 104 """ 105 105 raise NotImplementedError() 106 106 107 def date_interval_sql(self, sql, connector, modifiers): 108 """ 109 Implements the interval functionality for expressions 110 """ 111 mods = u','.join([u'"%s%s"' % (connector, modifier) for modifier in modifiers]) 112 return u'DATE(%s, %s)' % (sql, mods) 113 107 114 def date_trunc_sql(self, lookup_type, field_name): 108 115 """ 109 116 Given a lookup_type of 'year', 'month' or 'day', returns the SQL that -
tests/modeltests/expressions/models.py
7 7 class Employee(models.Model): 8 8 firstname = models.CharField(max_length=50) 9 9 lastname = models.CharField(max_length=50) 10 contract_date = models.DateField(null=True) 11 start_date = models.DateField(null=True) 10 12 11 13 def __unicode__(self): 12 14 return u'%s %s' % (self.firstname, self.lastname) … … 30 32 __test__ = {'API_TESTS': """ 31 33 >>> from django.db.models import F 32 34 35 >>> import datetime 36 33 37 >>> Company(name='Example Inc.', num_employees=2300, num_chairs=5, 34 ... ceo=Employee.objects.create(firstname='Joe', lastname='Smith')).save() 38 ... ceo=Employee.objects.create(firstname='Joe', lastname='Smith', 39 ... contract_date=datetime.date(2008,1,1), start_date=datetime.date(2008,3,1))).save() 35 40 >>> Company(name='Foobar Ltd.', num_employees=3, num_chairs=3, 36 ... ceo=Employee.objects.create(firstname='Frank', lastname='Meyer')).save() 41 ... ceo=Employee.objects.create(firstname='Frank', lastname='Meyer', 42 ... contract_date=datetime.date(2008,1,1), start_date=datetime.date(2008,5,1))).save() 37 43 >>> Company(name='Test GmbH', num_employees=32, num_chairs=1, 38 ... ceo=Employee.objects.create(firstname='Max', lastname='Mustermann')).save() 44 ... ceo=Employee.objects.create(firstname='Max', lastname='Mustermann', 45 ... contract_date=datetime.date(2008,1,1), start_date=datetime.date(2008,7,1))).save() 39 46 40 47 # We can filter for companies where the number of employees is greater than the 41 48 # number of chairs. … … 68 75 ... 69 76 FieldError: Joined field references are not permitted in this query 70 77 78 # F expressions for dates may be combined with timedelta 79 80 >>> Employee.objects.filter(start_date__gt=F('contract_date')+datetime.timedelta(days=80, seconds=500)) 81 [<Employee: Frank Meyer>, <Employee: Max Mustermann>] 82 83 >>> Employee.objects.filter(contract_date__gt=F('start_date')-datetime.timedelta(days=80, seconds=500)) 84 [<Employee: Joe Smith>] 85 71 86 """}