1 | import copy
|
---|
2 |
|
---|
3 | from django.contrib.auth.models import User
|
---|
4 | from django.db import models
|
---|
5 | from django.db.models.query import Q
|
---|
6 | from django.utils.datastructures import SortedDict
|
---|
7 | from django.core.cache import cache
|
---|
8 |
|
---|
9 | class RevisionableModel(models.Model):
|
---|
10 | base = models.ForeignKey('self', null=True)
|
---|
11 | title = models.CharField(blank=True, max_length=255)
|
---|
12 |
|
---|
13 | def __unicode__(self):
|
---|
14 | return u"%s (%s, %s)" % (self.title, self.id, self.base.id)
|
---|
15 |
|
---|
16 | def save(self):
|
---|
17 | super(RevisionableModel, self).save()
|
---|
18 | if not self.base:
|
---|
19 | self.base = self
|
---|
20 | super(RevisionableModel, self).save()
|
---|
21 |
|
---|
22 | def new_revision(self):
|
---|
23 | new_revision = copy.copy(self)
|
---|
24 | new_revision.pk = None
|
---|
25 | return new_revision
|
---|
26 |
|
---|
27 | class Order(models.Model):
|
---|
28 | created_by = models.ForeignKey(User)
|
---|
29 | text = models.TextField()
|
---|
30 |
|
---|
31 | __test__ = {"API_TESTS": """
|
---|
32 | # Regression tests for #7314 and #7372
|
---|
33 |
|
---|
34 | >>> rm = RevisionableModel.objects.create(title='First Revision')
|
---|
35 | >>> rm.pk, rm.base.pk
|
---|
36 | (1, 1)
|
---|
37 |
|
---|
38 | >>> rm2 = rm.new_revision()
|
---|
39 | >>> rm2.title = "Second Revision"
|
---|
40 | >>> rm2.save()
|
---|
41 | >>> print u"%s of %s" % (rm2.title, rm2.base.title)
|
---|
42 | Second Revision of First Revision
|
---|
43 |
|
---|
44 | >>> rm2.pk, rm2.base.pk
|
---|
45 | (2, 1)
|
---|
46 |
|
---|
47 | Queryset to match most recent revision:
|
---|
48 | >>> qs = RevisionableModel.objects.extra(where=["%(table)s.id IN (SELECT MAX(rev.id) FROM %(table)s rev GROUP BY rev.base_id)" % {'table': RevisionableModel._meta.db_table,}],)
|
---|
49 | >>> qs
|
---|
50 | [<RevisionableModel: Second Revision (2, 1)>]
|
---|
51 |
|
---|
52 | Queryset to search for string in title:
|
---|
53 | >>> qs2 = RevisionableModel.objects.filter(title__contains="Revision")
|
---|
54 | >>> qs2
|
---|
55 | [<RevisionableModel: First Revision (1, 1)>, <RevisionableModel: Second Revision (2, 1)>]
|
---|
56 |
|
---|
57 | Following queryset should return the most recent revision:
|
---|
58 | >>> qs & qs2
|
---|
59 | [<RevisionableModel: Second Revision (2, 1)>]
|
---|
60 |
|
---|
61 | >>> u = User.objects.create_user(username="fred", password="secret", email="fred@example.com")
|
---|
62 |
|
---|
63 | # General regression tests: extra select parameters should stay tied to their
|
---|
64 | # corresponding select portions. Applies when portions are updated or otherwise
|
---|
65 | # moved around.
|
---|
66 | >>> qs = User.objects.extra(select=SortedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))), select_params=(1, 3))
|
---|
67 | >>> qs = qs.extra(select={"beta": 4})
|
---|
68 | >>> qs = qs.extra(select={"alpha": "%s"}, select_params=[5])
|
---|
69 | >>> result = {'alpha': 5, 'beta': 4, 'gamma': 3}
|
---|
70 | >>> list(qs.filter(id=u.id).values('alpha', 'beta', 'gamma')) == [result]
|
---|
71 | True
|
---|
72 |
|
---|
73 | # Regression test for #7957: Combining extra() calls should leave the
|
---|
74 | # corresponding parameters associated with the right extra() bit. I.e. internal
|
---|
75 | # dictionary must remain sorted.
|
---|
76 | >>> User.objects.extra(select={"alpha": "%s"}, select_params=(1,)).extra(select={"beta": "%s"}, select_params=(2,))[0].alpha
|
---|
77 | 1
|
---|
78 | >>> User.objects.extra(select={"beta": "%s"}, select_params=(1,)).extra(select={"alpha": "%s"}, select_params=(2,))[0].alpha
|
---|
79 | 2
|
---|
80 |
|
---|
81 | # Regression test for #7961: When not using a portion of an extra(...) in a
|
---|
82 | # query, remove any corresponding parameters from the query as well.
|
---|
83 | >>> list(User.objects.extra(select={"alpha": "%s"}, select_params=(-6,)).filter(id=u.id).values_list('id', flat=True)) == [u.id]
|
---|
84 | True
|
---|
85 |
|
---|
86 | # Regression test for #8063: limiting a query shouldn't discard any extra()
|
---|
87 | # bits.
|
---|
88 | >>> qs = User.objects.all().extra(where=['id=%s'], params=[u.id])
|
---|
89 | >>> qs
|
---|
90 | [<User: fred>]
|
---|
91 | >>> qs[:1]
|
---|
92 | [<User: fred>]
|
---|
93 |
|
---|
94 | # Regression test for #8039: Ordering sometimes removed relevant tables from
|
---|
95 | # extra(). This test is the critical case: ordering uses a table, but then
|
---|
96 | # removes the reference because of an optimisation. The table should still be
|
---|
97 | # present because of the extra() call.
|
---|
98 | >>> Order.objects.extra(where=["username=%s"], params=["fred"], tables=["auth_user"]).order_by('created_by')
|
---|
99 | []
|
---|
100 |
|
---|
101 | ##################################
|
---|
102 | # Pickling a queryset with extra #
|
---|
103 | ##################################
|
---|
104 | >>> qs = RevisionableModel.objects.extra(select={"rm_count": "SELECT COUNT(*) from extra_regress_revisionablemodel"})
|
---|
105 | >>> cache.set("extra_test", qs)
|
---|
106 | >>> new_qs = cache.get("extra_test")
|
---|
107 |
|
---|
108 | """}
|
---|