diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/modeltests/expressions/__init__.py | 0 | ||||
| -rw-r--r-- | tests/modeltests/expressions/models.py | 71 | ||||
| -rw-r--r-- | tests/regressiontests/aggregation_regress/models.py | 17 | ||||
| -rw-r--r-- | tests/regressiontests/expressions_regress/__init__.py | 0 | ||||
| -rw-r--r-- | tests/regressiontests/expressions_regress/models.py | 133 |
5 files changed, 220 insertions, 1 deletions
diff --git a/tests/modeltests/expressions/__init__.py b/tests/modeltests/expressions/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/modeltests/expressions/__init__.py diff --git a/tests/modeltests/expressions/models.py b/tests/modeltests/expressions/models.py new file mode 100644 index 0000000000..4043f5ec34 --- /dev/null +++ b/tests/modeltests/expressions/models.py @@ -0,0 +1,71 @@ +""" +Tests for F() query expression syntax. +""" + +from django.db import models + +class Employee(models.Model): + firstname = models.CharField(max_length=50) + lastname = models.CharField(max_length=50) + + def __unicode__(self): + return u'%s %s' % (self.firstname, self.lastname) + +class Company(models.Model): + name = models.CharField(max_length=100) + num_employees = models.PositiveIntegerField() + num_chairs = models.PositiveIntegerField() + ceo = models.ForeignKey( + Employee, + related_name='company_ceo_set') + point_of_contact = models.ForeignKey( + Employee, + related_name='company_point_of_contact_set', + null=True) + + def __unicode__(self): + return self.name + + +__test__ = {'API_TESTS': """ +>>> from django.db.models import F + +>>> Company(name='Example Inc.', num_employees=2300, num_chairs=5, +... ceo=Employee.objects.create(firstname='Joe', lastname='Smith')).save() +>>> Company(name='Foobar Ltd.', num_employees=3, num_chairs=3, +... ceo=Employee.objects.create(firstname='Frank', lastname='Meyer')).save() +>>> Company(name='Test GmbH', num_employees=32, num_chairs=1, +... ceo=Employee.objects.create(firstname='Max', lastname='Mustermann')).save() + +# We can filter for companies where the number of employees is greater than the +# number of chairs. + +>>> Company.objects.filter(num_employees__gt=F('num_chairs')) +[<Company: Example Inc.>, <Company: Test GmbH>] + +# The relation of a foreign key can become copied over to an other foreign key. + +>>> Company.objects.update(point_of_contact=F('ceo')) +3 + +>>> [c.point_of_contact for c in Company.objects.all()] +[<Employee: Joe Smith>, <Employee: Frank Meyer>, <Employee: Max Mustermann>] + +>>> c = Company.objects.all()[0] +>>> c.point_of_contact = Employee.objects.create(firstname="Guido", lastname="van Rossum") +>>> c.save() + +# F Expressions can also span joins +>>> Company.objects.filter(ceo__firstname=F('point_of_contact__firstname')).distinct() +[<Company: Foobar Ltd.>, <Company: Test GmbH>] + +>>> _ = Company.objects.exclude(ceo__firstname=F('point_of_contact__firstname')).update(name='foo') +>>> Company.objects.exclude(ceo__firstname=F('point_of_contact__firstname')).get().name +u'foo' + +>>> _ = Company.objects.exclude(ceo__firstname=F('point_of_contact__firstname')).update(name=F('point_of_contact__lastname')) +Traceback (most recent call last): +... +FieldError: Joined field references are not permitted in this query + +"""} diff --git a/tests/regressiontests/aggregation_regress/models.py b/tests/regressiontests/aggregation_regress/models.py index a87ce1cd1d..c8a5473f4f 100644 --- a/tests/regressiontests/aggregation_regress/models.py +++ b/tests/regressiontests/aggregation_regress/models.py @@ -50,7 +50,7 @@ class Store(models.Model): #Extra does not play well with values. Modify the tests if/when this is fixed. __test__ = {'API_TESTS': """ >>> from django.core import management ->>> from django.db.models import get_app +>>> from django.db.models import get_app, F # Reset the database representation of this app. # This will return the database to a clean initial state. @@ -164,6 +164,21 @@ FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, id, i >>> len(Book.objects.annotate(num_authors=Count('authors')).exclude(num_authors__lt=2).filter(num_authors__lt=3)) 2 +# Aggregates can be used with F() expressions +# ... where the F() is pushed into the HAVING clause +>>> Publisher.objects.annotate(num_books=Count('book')).filter(num_books__lt=F('num_awards')/2).values('name','num_books','num_awards') +[{'num_books': 2, 'name': u'Prentice Hall', 'num_awards': 7}, {'num_books': 1, 'name': u'Morgan Kaufmann', 'num_awards': 9}] + +>>> Publisher.objects.annotate(num_books=Count('book')).exclude(num_books__lt=F('num_awards')/2).values('name','num_books','num_awards') +[{'num_books': 2, 'name': u'Apress', 'num_awards': 3}, {'num_books': 1, 'name': u'Sams', 'num_awards': 1}, {'num_books': 0, 'name': u"Jonno's House of Books", 'num_awards': 0}] + +# ... and where the F() references an aggregate +>>> Publisher.objects.annotate(num_books=Count('book')).filter(num_awards__gt=2*F('num_books')).values('name','num_books','num_awards') +[{'num_books': 2, 'name': u'Prentice Hall', 'num_awards': 7}, {'num_books': 1, 'name': u'Morgan Kaufmann', 'num_awards': 9}] + +>>> Publisher.objects.annotate(num_books=Count('book')).exclude(num_books__lt=F('num_awards')/2).values('name','num_books','num_awards') +[{'num_books': 2, 'name': u'Apress', 'num_awards': 3}, {'num_books': 1, 'name': u'Sams', 'num_awards': 1}, {'num_books': 0, 'name': u"Jonno's House of Books", 'num_awards': 0}] + # Regression for #10089: Check handling of empty result sets with aggregates >>> Book.objects.filter(id__in=[]).count() 0 diff --git a/tests/regressiontests/expressions_regress/__init__.py b/tests/regressiontests/expressions_regress/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/expressions_regress/__init__.py diff --git a/tests/regressiontests/expressions_regress/models.py b/tests/regressiontests/expressions_regress/models.py new file mode 100644 index 0000000000..2edab34b64 --- /dev/null +++ b/tests/regressiontests/expressions_regress/models.py @@ -0,0 +1,133 @@ +""" +Spanning tests for all the operations that F() expressions can perform. +""" + +from django.db import models + +# +# Model for testing arithmetic expressions. +# + +class Number(models.Model): + integer = models.IntegerField() + float = models.FloatField(null=True) + + def __unicode__(self): + return u'%i, %.3f' % (self.integer, self.float) + + +__test__ = {'API_TESTS': """ +>>> from django.db.models import F + +>>> Number(integer=-1).save() +>>> Number(integer=42).save() +>>> Number(integer=1337).save() + +We can fill a value in all objects with an other value of the same object. + +>>> Number.objects.update(float=F('integer')) +3 +>>> Number.objects.all() +[<Number: -1, -1.000>, <Number: 42, 42.000>, <Number: 1337, 1337.000>] + +We can increment a value of all objects in a query set. + +>>> Number.objects.filter(integer__gt=0).update(integer=F('integer') + 1) +2 +>>> Number.objects.all() +[<Number: -1, -1.000>, <Number: 43, 42.000>, <Number: 1338, 1337.000>] + +We can filter for objects, where a value is not equals the value of an other field. + +>>> Number.objects.exclude(float=F('integer')) +[<Number: 43, 42.000>, <Number: 1338, 1337.000>] + +Complex expressions of different connection types are possible. + +>>> n = Number.objects.create(integer=10, float=123.45) + +>>> Number.objects.filter(pk=n.pk).update(float=F('integer') + F('float') * 2) +1 +>>> Number.objects.get(pk=n.pk) +<Number: 10, 256.900> + +# All supported operators work as expected. + +>>> n = Number.objects.create(integer=42, float=15.5) + +# Left hand operators + +>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5) +>>> _ = Number.objects.filter(pk=n.pk).update(integer=F('integer') + 15, float=F('float') + 42.7) +>>> Number.objects.get(pk=n.pk) # LH Addition of floats and integers +<Number: 57, 58.200> + +>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5) +>>> _ = Number.objects.filter(pk=n.pk).update(integer=F('integer') - 15, float=F('float') - 42.7) +>>> Number.objects.get(pk=n.pk) # LH Subtraction of floats and integers +<Number: 27, -27.200> + +>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5) +>>> _ = Number.objects.filter(pk=n.pk).update(integer=F('integer') * 15, float=F('float') * 42.7) +>>> Number.objects.get(pk=n.pk) # Multiplication of floats and integers +<Number: 630, 661.850> + +>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5) +>>> _ = Number.objects.filter(pk=n.pk).update(integer=F('integer') / 2, float=F('float') / 42.7) +>>> Number.objects.get(pk=n.pk) # LH Division of floats and integers +<Number: 21, 0.363> + +>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5) +>>> _ = Number.objects.filter(pk=n.pk).update(integer=F('integer') % 20) +>>> Number.objects.get(pk=n.pk) # LH Modulo arithmetic on integers +<Number: 2, 15.500> + +>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5) +>>> _ = Number.objects.filter(pk=n.pk).update(integer=F('integer') & 56) +>>> Number.objects.get(pk=n.pk) # LH Bitwise ands on integers +<Number: 40, 15.500> + +>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5) +>>> _ = Number.objects.filter(pk=n.pk).update(integer=F('integer') | 48) +>>> Number.objects.get(pk=n.pk) # LH Bitwise or on integers +<Number: 58, 15.500> + +# Right hand operators + +>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5) +>>> _ = Number.objects.filter(pk=n.pk).update(integer=15 + F('integer'), float=42.7 + F('float')) +>>> Number.objects.get(pk=n.pk) # RH Addition of floats and integers +<Number: 57, 58.200> + +>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5) +>>> _ = Number.objects.filter(pk=n.pk).update(integer=15 - F('integer'), float=42.7 - F('float')) +>>> Number.objects.get(pk=n.pk) # RH Subtraction of floats and integers +<Number: -27, 27.200> + +>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5) +>>> _ = Number.objects.filter(pk=n.pk).update(integer=15 * F('integer'), float=42.7 * F('float')) +>>> Number.objects.get(pk=n.pk) # RH Multiplication of floats and integers +<Number: 630, 661.850> + +>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5) +>>> _ = Number.objects.filter(pk=n.pk).update(integer=640 / F('integer'), float=42.7 / F('float')) +>>> Number.objects.get(pk=n.pk) # RH Division of floats and integers +<Number: 15, 2.755> + +>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5) +>>> _ = Number.objects.filter(pk=n.pk).update(integer=69 % F('integer')) +>>> Number.objects.get(pk=n.pk) # RH Modulo arithmetic on integers +<Number: 27, 15.500> + +>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5) +>>> _ = Number.objects.filter(pk=n.pk).update(integer=15 & F('integer')) +>>> Number.objects.get(pk=n.pk) # RH Bitwise ands on integers +<Number: 10, 15.500> + +>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5) +>>> _ = Number.objects.filter(pk=n.pk).update(integer=15 | F('integer')) +>>> Number.objects.get(pk=n.pk) # RH Bitwise or on integers +<Number: 47, 15.500> + + +"""} |
