Ticket #10154: 10154_dateexpressions_3.diff
File 10154_dateexpressions_3.diff, 8.0 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, negated=False): 130 if len(children) != 2: 131 raise TypeError('You have to specify a node and a timedelta.') 132 if not isinstance(children[1], datetime.timedelta): 133 raise TypeError('Second child must be a timedelta.') 134 if connector is None: 135 raise TypeError('You have to specify a connector.') 136 super(DateModifierNode, self).__init__(children, connector, negated) 137 138 def prepare(self, evaluator, query, allow_joins): 139 return evaluator.prepare_node(self, query, allow_joins) 140 141 def evaluate(self, evaluator, qn): 142 if not qn: 143 qn = connection.ops.quote_name 144 145 timedelta = self.children.pop() 146 sql, params = evaluator.evaluate_node(self, qn) 147 148 return connection.ops.date_interval_sql(sql, self.connector, timedelta), params 149 -
django/db/backends/postgresql/operations.py
33 33 else: 34 34 return "EXTRACT('%s' FROM %s)" % (lookup_type, field_name) 35 35 36 def date_interval_sql(self, sql, connector, timedelta): 37 """ 38 implements the interval functionality for expressions 39 format for Postgres: (datefield + interval '3 days 200 seconds') 40 """ 41 modifiers = [] 42 if timedelta.days: 43 modifiers.append(u'%s days' % timedelta.days) 44 if timedelta.seconds: 45 modifiers.append(u'%s seconds' % timedelta.seconds) 46 mods = u' '.join(modifiers) 47 conn = u' %s ' % connector 48 return u'(%s)' % conn.join([sql, u'interval \'%s\'' % mods]) 49 36 50 def date_trunc_sql(self, lookup_type, field_name): 37 51 # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC 38 52 return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) -
django/db/backends/oracle/base.py
78 78 else: 79 79 return "EXTRACT(%s FROM %s)" % (lookup_type, field_name) 80 80 81 def date_interval_sql(self, sql, connector, timedelta): 82 """ 83 Implements the interval functionality for expressions 84 format for Oracle: 85 (datefield + INTERVAL '3 00:03:20.000000' DAY(1) TO SECOND(6)) 86 """ 87 minutes, seconds = divmod(timedelta.seconds, 60) 88 hours, minutes = divmod(minutes, 60) 89 days = str(timedelta.days) 90 day_precision = len(days) 91 fmt = "(%s %s INTERVAL '%s %02d:%02d:%02d.%06d' DAY(%d) TO SECOND(6))" 92 return fmt % (sql, connector, days, hours, minutes, seconds, 93 timedelta.microseconds, day_precision) 94 81 95 def date_trunc_sql(self, lookup_type, field_name): 82 96 # Oracle uses TRUNC() for both dates and numbers. 83 97 # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions155a.htm#SQLRF06151 -
django/db/backends/__init__.py
104 104 """ 105 105 raise NotImplementedError() 106 106 107 def date_interval_sql(self, sql, connector, timedelta): 108 """ 109 Implements the interval functionality for expressions 110 format for sqlite: DATE(datefield, "3 days", "200 seconds") 111 """ 112 modifiers = [] 113 if timedelta.days: 114 modifiers.append(u'%s days' % timedelta.days) 115 if timedelta.seconds: 116 modifiers.append(u'%s seconds' % timedelta.seconds) 117 mods = u','.join([u'"%s%s"' % (connector, modifier) for modifier in modifiers]) 118 return u'DATE(%s, %s)' % (sql, mods) 119 107 120 def date_trunc_sql(self, lookup_type, field_name): 108 121 """ 109 122 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 # in filter: 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 86 # chaining multiple timedeltas: 87 >>> Employee.objects.filter(start_date__gt=F('contract_date')+datetime.timedelta(days=80)+datetime.timedelta(seconds=500)) 88 [<Employee: Frank Meyer>, <Employee: Max Mustermann>] 89 90 # in update: 91 # move contractdates to 14 days before startdate 92 >>> Employee.objects.update(contract_date=F('start_date')-datetime.timedelta(days=14)) 93 4 94 >>> [e.contract_date for e in Employee.objects.all()] 95 [datetime.date(2008, 2, 16), datetime.date(2008, 4, 17), datetime.date(2008, 6, 17), None] 96 71 97 """}