Tests expressions DurationField ¶
Classe Experiment ¶
1 class Experiment(models.Model):
2 name = models.CharField(max_length=24)
3 assigned = models.DateField()
4 completed = models.DateField()
5 estimated_time = models.DurationField()
6 start = models.DateTimeField()
7 end = models.DateTimeField()
8
9 class Meta:
10 db_table = 'expressions_ExPeRiMeNt'
11 ordering = ('name',)
12
13 def duration(self):
14 return self.end - self.start
Classe Time ¶
1 class Time(models.Model):
2 time = models.TimeField(null=True)
3
4 def __str__(self):
5 return "%s" % self.time
class FTimeDeltaTests(TestCase) ¶
1 import datetime
2 import pickle
3 import unittest
4 import uuid
5 from copy import deepcopy
6 from unittest import mock
7
8 from django.core.exceptions import FieldError
9 from django.db import DatabaseError, connection
10 from django.db.models import (
11 Avg, BooleanField, Case, CharField, Count, DateField, DateTimeField,
12 DurationField, Exists, Expression, ExpressionList, ExpressionWrapper, F,
13 Func, IntegerField, Max, Min, Model, OrderBy, OuterRef, Q, StdDev,
14 Subquery, Sum, TimeField, UUIDField, Value, Variance, When,
15 )
16 from django.db.models.expressions import Col, Combinable, Random, RawSQL, Ref
17 from django.db.models.functions import (
18 Coalesce, Concat, Length, Lower, Substr, Upper,
19 )
20 from django.db.models.sql import constants
21 from django.db.models.sql.datastructures import Join
22 from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
23 from django.test.utils import Approximate, isolate_apps
24
25 from .models import (
26 UUID, UUIDPK, Company, Employee, Experiment, Number, RemoteEmployee,
27 Result, SimulationRun, Time,
28 )
29
30
31 class FTimeDeltaTests(TestCase):
32
33 @classmethod
34 def setUpTestData(cls):
35 cls.sday = sday = datetime.date(2010, 6, 25)
36 cls.stime = stime = datetime.datetime(2010, 6, 25, 12, 15, 30, 747000)
37 midnight = datetime.time(0)
38
39 delta0 = datetime.timedelta(0)
40 delta1 = datetime.timedelta(microseconds=253000)
41 delta2 = datetime.timedelta(seconds=44)
42 delta3 = datetime.timedelta(hours=21, minutes=8)
43 delta4 = datetime.timedelta(days=10)
44 delta5 = datetime.timedelta(days=90)
45
46 # Test data is set so that deltas and delays will be
47 # strictly increasing.
48 cls.deltas = []
49 cls.delays = []
50 cls.days_long = []
51
52 # e0: started same day as assigned, zero duration
53 end = stime + delta0
54 e0 = Experiment.objects.create(
55 name='e0', assigned=sday, start=stime, end=end,
56 completed=end.date(), estimated_time=delta0,
57 )
58 cls.deltas.append(delta0)
59 cls.delays.append(e0.start - datetime.datetime.combine(e0.assigned, midnight))
60 cls.days_long.append(e0.completed - e0.assigned)
61
62 # e1: started one day after assigned, tiny duration, data
63 # set so that end time has no fractional seconds, which
64 # tests an edge case on sqlite.
65 delay = datetime.timedelta(1)
66 end = stime + delay + delta1
67 e1 = Experiment.objects.create(
68 name='e1', assigned=sday, start=stime + delay, end=end,
69 completed=end.date(), estimated_time=delta1,
70 )
71 cls.deltas.append(delta1)
72 cls.delays.append(e1.start - datetime.datetime.combine(e1.assigned, midnight))
73 cls.days_long.append(e1.completed - e1.assigned)
74
75 # e2: started three days after assigned, small duration
76 end = stime + delta2
77 e2 = Experiment.objects.create(
78 name='e2', assigned=sday - datetime.timedelta(3), start=stime,
79 end=end, completed=end.date(), estimated_time=datetime.timedelta(hours=1),
80 )
81 cls.deltas.append(delta2)
82 cls.delays.append(e2.start - datetime.datetime.combine(e2.assigned, midnight))
83 cls.days_long.append(e2.completed - e2.assigned)
84
85 # e3: started four days after assigned, medium duration
86 delay = datetime.timedelta(4)
87 end = stime + delay + delta3
88 e3 = Experiment.objects.create(
89 name='e3', assigned=sday, start=stime + delay, end=end,
90 completed=end.date(), estimated_time=delta3,
91 )
92 cls.deltas.append(delta3)
93 cls.delays.append(e3.start - datetime.datetime.combine(e3.assigned, midnight))
94 cls.days_long.append(e3.completed - e3.assigned)
95
96 # e4: started 10 days after assignment, long duration
97 end = stime + delta4
98 e4 = Experiment.objects.create(
99 name='e4', assigned=sday - datetime.timedelta(10), start=stime,
100 end=end, completed=end.date(), estimated_time=delta4 - datetime.timedelta(1),
101 )
102 cls.deltas.append(delta4)
103 cls.delays.append(e4.start - datetime.datetime.combine(e4.assigned, midnight))
104 cls.days_long.append(e4.completed - e4.assigned)
105
106 # e5: started a month after assignment, very long duration
107 delay = datetime.timedelta(30)
108 end = stime + delay + delta5
109 e5 = Experiment.objects.create(
110 name='e5', assigned=sday, start=stime + delay, end=end,
111 completed=end.date(), estimated_time=delta5,
112 )
113 cls.deltas.append(delta5)
114 cls.delays.append(e5.start - datetime.datetime.combine(e5.assigned, midnight))
115 cls.days_long.append(e5.completed - e5.assigned)
116
117 cls.expnames = [e.name for e in Experiment.objects.all()]
test_durationfield_add ¶
1 def test_durationfield_add(self):
2 zeros = [e.name for e in Experiment.objects.filter(start=F('start') + F('estimated_time'))]
3 self.assertEqual(zeros, ['e0'])
4
5 end_less = [e.name for e in Experiment.objects.filter(end__lt=F('start') + F('estimated_time'))]
6 self.assertEqual(end_less, ['e2'])
7
8 delta_math = [
9 e.name for e in
10 Experiment.objects.filter(end__gte=F('start') + F('estimated_time') + datetime.timedelta(hours=1))
11 ]
12 self.assertEqual(delta_math, ['e4'])
13
14 queryset = Experiment.objects.annotate(shifted=ExpressionWrapper(
15 F('start') + Value(None, output_field=DurationField()),
16 output_field=DateTimeField(),
17 ))
18 self.assertIsNone(queryset.first().shifted)
test_date_subtraction ¶
1 @skipUnlessDBFeature('supports_temporal_subtraction')
2 def test_date_subtraction(self):
3 queryset = Experiment.objects.annotate(
4 completion_duration=ExpressionWrapper(
5 F('completed') - F('assigned'), output_field=DurationField()
6 )
7 )
8
9 at_least_5_days = {e.name for e in queryset.filter(completion_duration__gte=datetime.timedelta(days=5))}
10 self.assertEqual(at_least_5_days, {'e3', 'e4', 'e5'})
11
12 at_least_120_days = {e.name for e in queryset.filter(completion_duration__gte=datetime.timedelta(days=120))}
13 self.assertEqual(at_least_120_days, {'e5'})
14
15 less_than_5_days = {e.name for e in queryset.filter(completion_duration__lt=datetime.timedelta(days=5))}
16 self.assertEqual(less_than_5_days, {'e0', 'e1', 'e2'})
17
18 queryset = Experiment.objects.annotate(difference=ExpressionWrapper(
19 F('completed') - Value(None, output_field=DateField()),
20 output_field=DurationField(),
21 ))
22 self.assertIsNone(queryset.first().difference)
23
24 queryset = Experiment.objects.annotate(shifted=ExpressionWrapper(
25 F('completed') - Value(None, output_field=DurationField()),
26 output_field=DateField(),
27 ))
28 self.assertIsNone(queryset.first().shifted)
test_date_subquery_subtraction ¶
1 @skipUnlessDBFeature('supports_temporal_subtraction')
2 def test_date_subquery_subtraction(self):
3 subquery = Experiment.objects.filter(pk=OuterRef('pk')).values('completed')
4 queryset = Experiment.objects.annotate(
5 difference=ExpressionWrapper(
6 subquery - F('completed'), output_field=DurationField(),
7 ),
8 ).filter(difference=datetime.timedelta())
9 self.assertTrue(queryset.exists())
test_time_subquery_subtraction ¶
1 @skipUnlessDBFeature('supports_temporal_subtraction')
2 def test_time_subquery_subtraction(self):
3 Time.objects.create(time=datetime.time(12, 30, 15, 2345))
4 subquery = Time.objects.filter(pk=OuterRef('pk')).values('time')
5 queryset = Time.objects.annotate(
6 difference=ExpressionWrapper(
7 subquery - F('time'), output_field=DurationField(),
8 ),
9 ).filter(difference=datetime.timedelta())
10 self.assertTrue(queryset.exists())
test_datetime_subtraction_microseconds ¶
1 @skipUnlessDBFeature('supports_temporal_subtraction')
2 def test_datetime_subtraction_microseconds(self):
3 delta = datetime.timedelta(microseconds=8999999999999999)
4 Experiment.objects.update(end=F('start') + delta)
5 qs = Experiment.objects.annotate(
6 delta=ExpressionWrapper(F('end') - F('start'), output_field=DurationField())
7 )
8 for e in qs:
9 self.assertEqual(e.delta, delta)
test_duration_with_datetime ¶
1 def test_duration_with_datetime(self):
2 # Exclude e1 which has very high precision so we can test this on all
3 # backends regardless of whether or not it supports
4 # microsecond_precision.
5 over_estimate = Experiment.objects.exclude(name='e1').filter(
6 completed__gt=self.stime + F('estimated_time'),
7 ).order_by('name')
8 self.assertQuerysetEqual(over_estimate, ['e3', 'e4', 'e5'], lambda e: e.name)
test_duration_with_datetime_microseconds ¶
1 def test_duration_with_datetime_microseconds(self):
2 delta = datetime.timedelta(microseconds=8999999999999999)
3 qs = Experiment.objects.annotate(dt=ExpressionWrapper(
4 F('start') + delta,
5 output_field=DateTimeField(),
6 ))
7 for e in qs:
8 self.assertEqual(e.dt, e.start + delta)
test_date_minus_duration ¶
1 def test_date_minus_duration(self):
2 more_than_4_days = Experiment.objects.filter(
3 assigned__lt=F('completed') - Value(datetime.timedelta(days=4), output_field=DurationField())
4 )
5 self.assertQuerysetEqual(more_than_4_days, ['e3', 'e4', 'e5'], lambda e: e.name)
test_negative_timedelta_update ¶
1 def test_negative_timedelta_update(self):
2 # subtract 30 seconds, 30 minutes, 2 hours and 2 days
3 experiments = Experiment.objects.filter(name='e0').annotate(
4 start_sub_seconds=F('start') + datetime.timedelta(seconds=-30),
5 ).annotate(
6 start_sub_minutes=F('start_sub_seconds') + datetime.timedelta(minutes=-30),
7 ).annotate(
8 start_sub_hours=F('start_sub_minutes') + datetime.timedelta(hours=-2),
9 ).annotate(
10 new_start=F('start_sub_hours') + datetime.timedelta(days=-2),
11 )
12 expected_start = datetime.datetime(2010, 6, 23, 9, 45, 0)
13 # subtract 30 microseconds
14 experiments = experiments.annotate(new_start=F('new_start') + datetime.timedelta(microseconds=-30))
15 expected_start += datetime.timedelta(microseconds=+746970)
16 experiments.update(start=F('new_start'))
17 e0 = Experiment.objects.get(name='e0')
18 self.assertEqual(e0.start, expected_start)