App Engine Python SDK version 1.7.4 (2)
[gae.git] / python / lib / django_1_4 / tests / modeltests / generic_relations / tests.py
blob0ac552cf77099c2f78c5a6c9d211ff30e2404fb9
1 from __future__ import absolute_import
3 from django import forms
4 from django.contrib.contenttypes.generic import generic_inlineformset_factory
5 from django.contrib.contenttypes.models import ContentType
6 from django.test import TestCase
8 from .models import (TaggedItem, ValuableTaggedItem, Comparison, Animal,
9 Vegetable, Mineral, Gecko)
12 class GenericRelationsTests(TestCase):
13 def test_generic_relations(self):
14 # Create the world in 7 lines of code...
15 lion = Animal.objects.create(common_name="Lion", latin_name="Panthera leo")
16 platypus = Animal.objects.create(
17 common_name="Platypus", latin_name="Ornithorhynchus anatinus"
19 eggplant = Vegetable.objects.create(name="Eggplant", is_yucky=True)
20 bacon = Vegetable.objects.create(name="Bacon", is_yucky=False)
21 quartz = Mineral.objects.create(name="Quartz", hardness=7)
23 # Objects with declared GenericRelations can be tagged directly -- the
24 # API mimics the many-to-many API.
25 bacon.tags.create(tag="fatty")
26 bacon.tags.create(tag="salty")
27 lion.tags.create(tag="yellow")
28 lion.tags.create(tag="hairy")
29 platypus.tags.create(tag="fatty")
30 self.assertQuerysetEqual(lion.tags.all(), [
31 "<TaggedItem: hairy>",
32 "<TaggedItem: yellow>"
34 self.assertQuerysetEqual(bacon.tags.all(), [
35 "<TaggedItem: fatty>",
36 "<TaggedItem: salty>"
39 # You can easily access the content object like a foreign key.
40 t = TaggedItem.objects.get(tag="salty")
41 self.assertEqual(t.content_object, bacon)
43 # Recall that the Mineral class doesn't have an explicit GenericRelation
44 # defined. That's OK, because you can create TaggedItems explicitly.
45 tag1 = TaggedItem.objects.create(content_object=quartz, tag="shiny")
46 tag2 = TaggedItem.objects.create(content_object=quartz, tag="clearish")
48 # However, excluding GenericRelations means your lookups have to be a
49 # bit more explicit.
50 ctype = ContentType.objects.get_for_model(quartz)
51 q = TaggedItem.objects.filter(
52 content_type__pk=ctype.id, object_id=quartz.id
54 self.assertQuerysetEqual(q, [
55 "<TaggedItem: clearish>",
56 "<TaggedItem: shiny>"
59 # You can set a generic foreign key in the way you'd expect.
60 tag1.content_object = platypus
61 tag1.save()
62 self.assertQuerysetEqual(platypus.tags.all(), [
63 "<TaggedItem: fatty>",
64 "<TaggedItem: shiny>"
66 q = TaggedItem.objects.filter(
67 content_type__pk=ctype.id, object_id=quartz.id
69 self.assertQuerysetEqual(q, ["<TaggedItem: clearish>"])
71 # Queries across generic relations respect the content types. Even
72 # though there are two TaggedItems with a tag of "fatty", this query
73 # only pulls out the one with the content type related to Animals.
74 self.assertQuerysetEqual(Animal.objects.order_by('common_name'), [
75 "<Animal: Lion>",
76 "<Animal: Platypus>"
78 self.assertQuerysetEqual(Animal.objects.filter(tags__tag='fatty'), [
79 "<Animal: Platypus>"
81 self.assertQuerysetEqual(Animal.objects.exclude(tags__tag='fatty'), [
82 "<Animal: Lion>"
85 # If you delete an object with an explicit Generic relation, the related
86 # objects are deleted when the source object is deleted.
87 # Original list of tags:
88 comp_func = lambda obj: (
89 obj.tag, obj.content_type.model_class(), obj.object_id
92 self.assertQuerysetEqual(TaggedItem.objects.all(), [
93 (u'clearish', Mineral, quartz.pk),
94 (u'fatty', Animal, platypus.pk),
95 (u'fatty', Vegetable, bacon.pk),
96 (u'hairy', Animal, lion.pk),
97 (u'salty', Vegetable, bacon.pk),
98 (u'shiny', Animal, platypus.pk),
99 (u'yellow', Animal, lion.pk)
101 comp_func
103 lion.delete()
104 self.assertQuerysetEqual(TaggedItem.objects.all(), [
105 (u'clearish', Mineral, quartz.pk),
106 (u'fatty', Animal, platypus.pk),
107 (u'fatty', Vegetable, bacon.pk),
108 (u'salty', Vegetable, bacon.pk),
109 (u'shiny', Animal, platypus.pk)
111 comp_func
114 # If Generic Relation is not explicitly defined, any related objects
115 # remain after deletion of the source object.
116 quartz_pk = quartz.pk
117 quartz.delete()
118 self.assertQuerysetEqual(TaggedItem.objects.all(), [
119 (u'clearish', Mineral, quartz_pk),
120 (u'fatty', Animal, platypus.pk),
121 (u'fatty', Vegetable, bacon.pk),
122 (u'salty', Vegetable, bacon.pk),
123 (u'shiny', Animal, platypus.pk)
125 comp_func
127 # If you delete a tag, the objects using the tag are unaffected
128 # (other than losing a tag)
129 tag = TaggedItem.objects.order_by("id")[0]
130 tag.delete()
131 self.assertQuerysetEqual(bacon.tags.all(), ["<TaggedItem: salty>"])
132 self.assertQuerysetEqual(TaggedItem.objects.all(), [
133 (u'clearish', Mineral, quartz_pk),
134 (u'fatty', Animal, platypus.pk),
135 (u'salty', Vegetable, bacon.pk),
136 (u'shiny', Animal, platypus.pk)
138 comp_func
140 TaggedItem.objects.filter(tag='fatty').delete()
141 ctype = ContentType.objects.get_for_model(lion)
142 self.assertQuerysetEqual(Animal.objects.filter(tags__content_type=ctype), [
143 "<Animal: Platypus>"
147 def test_multiple_gfk(self):
148 # Simple tests for multiple GenericForeignKeys
149 # only uses one model, since the above tests should be sufficient.
150 tiger = Animal.objects.create(common_name="tiger")
151 cheetah = Animal.objects.create(common_name="cheetah")
152 bear = Animal.objects.create(common_name="bear")
154 # Create directly
155 Comparison.objects.create(
156 first_obj=cheetah, other_obj=tiger, comparative="faster"
158 Comparison.objects.create(
159 first_obj=tiger, other_obj=cheetah, comparative="cooler"
162 # Create using GenericRelation
163 tiger.comparisons.create(other_obj=bear, comparative="cooler")
164 tiger.comparisons.create(other_obj=cheetah, comparative="stronger")
165 self.assertQuerysetEqual(cheetah.comparisons.all(), [
166 "<Comparison: cheetah is faster than tiger>"
169 # Filtering works
170 self.assertQuerysetEqual(tiger.comparisons.filter(comparative="cooler"), [
171 "<Comparison: tiger is cooler than cheetah>",
172 "<Comparison: tiger is cooler than bear>"
175 # Filtering and deleting works
176 subjective = ["cooler"]
177 tiger.comparisons.filter(comparative__in=subjective).delete()
178 self.assertQuerysetEqual(Comparison.objects.all(), [
179 "<Comparison: cheetah is faster than tiger>",
180 "<Comparison: tiger is stronger than cheetah>"
183 # If we delete cheetah, Comparisons with cheetah as 'first_obj' will be
184 # deleted since Animal has an explicit GenericRelation to Comparison
185 # through first_obj. Comparisons with cheetah as 'other_obj' will not
186 # be deleted.
187 cheetah.delete()
188 self.assertQuerysetEqual(Comparison.objects.all(), [
189 "<Comparison: tiger is stronger than None>"
192 def test_gfk_subclasses(self):
193 # GenericForeignKey should work with subclasses (see #8309)
194 quartz = Mineral.objects.create(name="Quartz", hardness=7)
195 valuedtag = ValuableTaggedItem.objects.create(
196 content_object=quartz, tag="shiny", value=10
198 self.assertEqual(valuedtag.content_object, quartz)
200 def test_generic_inline_formsets(self):
201 GenericFormSet = generic_inlineformset_factory(TaggedItem, extra=1)
202 formset = GenericFormSet()
203 self.assertHTMLEqual(u''.join(form.as_p() for form in formset.forms), u"""<p><label for="id_generic_relations-taggeditem-content_type-object_id-0-tag">Tag:</label> <input id="id_generic_relations-taggeditem-content_type-object_id-0-tag" type="text" name="generic_relations-taggeditem-content_type-object_id-0-tag" maxlength="50" /></p>
204 <p><label for="id_generic_relations-taggeditem-content_type-object_id-0-DELETE">Delete:</label> <input type="checkbox" name="generic_relations-taggeditem-content_type-object_id-0-DELETE" id="id_generic_relations-taggeditem-content_type-object_id-0-DELETE" /><input type="hidden" name="generic_relations-taggeditem-content_type-object_id-0-id" id="id_generic_relations-taggeditem-content_type-object_id-0-id" /></p>""")
206 formset = GenericFormSet(instance=Animal())
207 self.assertHTMLEqual(u''.join(form.as_p() for form in formset.forms), u"""<p><label for="id_generic_relations-taggeditem-content_type-object_id-0-tag">Tag:</label> <input id="id_generic_relations-taggeditem-content_type-object_id-0-tag" type="text" name="generic_relations-taggeditem-content_type-object_id-0-tag" maxlength="50" /></p>
208 <p><label for="id_generic_relations-taggeditem-content_type-object_id-0-DELETE">Delete:</label> <input type="checkbox" name="generic_relations-taggeditem-content_type-object_id-0-DELETE" id="id_generic_relations-taggeditem-content_type-object_id-0-DELETE" /><input type="hidden" name="generic_relations-taggeditem-content_type-object_id-0-id" id="id_generic_relations-taggeditem-content_type-object_id-0-id" /></p>""")
210 platypus = Animal.objects.create(
211 common_name="Platypus", latin_name="Ornithorhynchus anatinus"
213 platypus.tags.create(tag="shiny")
214 GenericFormSet = generic_inlineformset_factory(TaggedItem, extra=1)
215 formset = GenericFormSet(instance=platypus)
216 tagged_item_id = TaggedItem.objects.get(
217 tag='shiny', object_id=platypus.id
218 ).id
219 self.assertHTMLEqual(u''.join(form.as_p() for form in formset.forms), u"""<p><label for="id_generic_relations-taggeditem-content_type-object_id-0-tag">Tag:</label> <input id="id_generic_relations-taggeditem-content_type-object_id-0-tag" type="text" name="generic_relations-taggeditem-content_type-object_id-0-tag" value="shiny" maxlength="50" /></p>
220 <p><label for="id_generic_relations-taggeditem-content_type-object_id-0-DELETE">Delete:</label> <input type="checkbox" name="generic_relations-taggeditem-content_type-object_id-0-DELETE" id="id_generic_relations-taggeditem-content_type-object_id-0-DELETE" /><input type="hidden" name="generic_relations-taggeditem-content_type-object_id-0-id" value="%s" id="id_generic_relations-taggeditem-content_type-object_id-0-id" /></p><p><label for="id_generic_relations-taggeditem-content_type-object_id-1-tag">Tag:</label> <input id="id_generic_relations-taggeditem-content_type-object_id-1-tag" type="text" name="generic_relations-taggeditem-content_type-object_id-1-tag" maxlength="50" /></p>
221 <p><label for="id_generic_relations-taggeditem-content_type-object_id-1-DELETE">Delete:</label> <input type="checkbox" name="generic_relations-taggeditem-content_type-object_id-1-DELETE" id="id_generic_relations-taggeditem-content_type-object_id-1-DELETE" /><input type="hidden" name="generic_relations-taggeditem-content_type-object_id-1-id" id="id_generic_relations-taggeditem-content_type-object_id-1-id" /></p>""" % tagged_item_id)
223 lion = Animal.objects.create(common_name="Lion", latin_name="Panthera leo")
224 formset = GenericFormSet(instance=lion, prefix='x')
225 self.assertHTMLEqual(u''.join(form.as_p() for form in formset.forms), u"""<p><label for="id_x-0-tag">Tag:</label> <input id="id_x-0-tag" type="text" name="x-0-tag" maxlength="50" /></p>
226 <p><label for="id_x-0-DELETE">Delete:</label> <input type="checkbox" name="x-0-DELETE" id="id_x-0-DELETE" /><input type="hidden" name="x-0-id" id="id_x-0-id" /></p>""")
228 def test_gfk_manager(self):
229 # GenericForeignKey should not use the default manager (which may filter objects) #16048
230 tailless = Gecko.objects.create(has_tail=False)
231 tag = TaggedItem.objects.create(content_object=tailless, tag="lizard")
232 self.assertEqual(tag.content_object, tailless)
234 class CustomWidget(forms.CharField):
235 pass
237 class TaggedItemForm(forms.ModelForm):
238 class Meta:
239 model = TaggedItem
240 widgets = {'tag': CustomWidget}
242 class GenericInlineFormsetTest(TestCase):
244 Regression for #14572: Using base forms with widgets
245 defined in Meta should not raise errors.
248 def test_generic_inlineformset_factory(self):
249 Formset = generic_inlineformset_factory(TaggedItem, TaggedItemForm)
250 form = Formset().forms[0]
251 self.assertTrue(isinstance(form['tag'].field.widget, CustomWidget))