parts/django/tests/modeltests/generic_relations/tests.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 from django.contrib.contenttypes.generic import generic_inlineformset_factory
       
     2 from django.contrib.contenttypes.models import ContentType
       
     3 from django.test import TestCase
       
     4 
       
     5 from models import (TaggedItem, ValuableTaggedItem, Comparison, Animal,
       
     6     Vegetable, Mineral)
       
     7 
       
     8 
       
     9 class GenericRelationsTests(TestCase):
       
    10     def test_generic_relations(self):
       
    11         # Create the world in 7 lines of code...
       
    12         lion = Animal.objects.create(common_name="Lion", latin_name="Panthera leo")
       
    13         platypus = Animal.objects.create(
       
    14             common_name="Platypus", latin_name="Ornithorhynchus anatinus"
       
    15         )
       
    16         eggplant = Vegetable.objects.create(name="Eggplant", is_yucky=True)
       
    17         bacon = Vegetable.objects.create(name="Bacon", is_yucky=False)
       
    18         quartz = Mineral.objects.create(name="Quartz", hardness=7)
       
    19 
       
    20         # Objects with declared GenericRelations can be tagged directly -- the
       
    21         # API mimics the many-to-many API.
       
    22         bacon.tags.create(tag="fatty")
       
    23         bacon.tags.create(tag="salty")
       
    24         lion.tags.create(tag="yellow")
       
    25         lion.tags.create(tag="hairy")
       
    26         platypus.tags.create(tag="fatty")
       
    27         self.assertQuerysetEqual(lion.tags.all(), [
       
    28             "<TaggedItem: hairy>",
       
    29             "<TaggedItem: yellow>"
       
    30         ])
       
    31         self.assertQuerysetEqual(bacon.tags.all(), [
       
    32             "<TaggedItem: fatty>",
       
    33             "<TaggedItem: salty>"
       
    34         ])
       
    35 
       
    36         # You can easily access the content object like a foreign key.
       
    37         t = TaggedItem.objects.get(tag="salty")
       
    38         self.assertEqual(t.content_object, bacon)
       
    39 
       
    40         # Recall that the Mineral class doesn't have an explicit GenericRelation
       
    41         # defined. That's OK, because you can create TaggedItems explicitly.
       
    42         tag1 = TaggedItem.objects.create(content_object=quartz, tag="shiny")
       
    43         tag2 = TaggedItem.objects.create(content_object=quartz, tag="clearish")
       
    44 
       
    45         # However, excluding GenericRelations means your lookups have to be a
       
    46         # bit more explicit.
       
    47         ctype = ContentType.objects.get_for_model(quartz)
       
    48         q = TaggedItem.objects.filter(
       
    49             content_type__pk=ctype.id, object_id=quartz.id
       
    50         )
       
    51         self.assertQuerysetEqual(q, [
       
    52             "<TaggedItem: clearish>",
       
    53             "<TaggedItem: shiny>"
       
    54         ])
       
    55 
       
    56         # You can set a generic foreign key in the way you'd expect.
       
    57         tag1.content_object = platypus
       
    58         tag1.save()
       
    59         self.assertQuerysetEqual(platypus.tags.all(), [
       
    60             "<TaggedItem: fatty>",
       
    61             "<TaggedItem: shiny>"
       
    62         ])
       
    63         q = TaggedItem.objects.filter(
       
    64             content_type__pk=ctype.id, object_id=quartz.id
       
    65         )
       
    66         self.assertQuerysetEqual(q, ["<TaggedItem: clearish>"])
       
    67 
       
    68         # Queries across generic relations respect the content types. Even
       
    69         # though there are two TaggedItems with a tag of "fatty", this query
       
    70         # only pulls out the one with the content type related to Animals.
       
    71         self.assertQuerysetEqual(Animal.objects.order_by('common_name'), [
       
    72             "<Animal: Lion>",
       
    73             "<Animal: Platypus>"
       
    74         ])
       
    75         self.assertQuerysetEqual(Animal.objects.filter(tags__tag='fatty'), [
       
    76             "<Animal: Platypus>"
       
    77         ])
       
    78         self.assertQuerysetEqual(Animal.objects.exclude(tags__tag='fatty'), [
       
    79             "<Animal: Lion>"
       
    80         ])
       
    81 
       
    82         # If you delete an object with an explicit Generic relation, the related
       
    83         # objects are deleted when the source object is deleted.
       
    84         # Original list of tags:
       
    85         comp_func = lambda obj: (
       
    86             obj.tag, obj.content_type.model_class(), obj.object_id
       
    87         )
       
    88 
       
    89         self.assertQuerysetEqual(TaggedItem.objects.all(), [
       
    90                 (u'clearish', Mineral, quartz.pk),
       
    91                 (u'fatty', Animal, platypus.pk),
       
    92                 (u'fatty', Vegetable, bacon.pk),
       
    93                 (u'hairy', Animal, lion.pk),
       
    94                 (u'salty', Vegetable, bacon.pk),
       
    95                 (u'shiny', Animal, platypus.pk),
       
    96                 (u'yellow', Animal, lion.pk)
       
    97             ],
       
    98             comp_func
       
    99         )
       
   100         lion.delete()
       
   101         self.assertQuerysetEqual(TaggedItem.objects.all(), [
       
   102                 (u'clearish', Mineral, quartz.pk),
       
   103                 (u'fatty', Animal, platypus.pk),
       
   104                 (u'fatty', Vegetable, bacon.pk),
       
   105                 (u'salty', Vegetable, bacon.pk),
       
   106                 (u'shiny', Animal, platypus.pk)
       
   107             ],
       
   108             comp_func
       
   109         )
       
   110 
       
   111         # If Generic Relation is not explicitly defined, any related objects
       
   112         # remain after deletion of the source object.
       
   113         quartz_pk = quartz.pk
       
   114         quartz.delete()
       
   115         self.assertQuerysetEqual(TaggedItem.objects.all(), [
       
   116                 (u'clearish', Mineral, quartz_pk),
       
   117                 (u'fatty', Animal, platypus.pk),
       
   118                 (u'fatty', Vegetable, bacon.pk),
       
   119                 (u'salty', Vegetable, bacon.pk),
       
   120                 (u'shiny', Animal, platypus.pk)
       
   121             ],
       
   122             comp_func
       
   123         )
       
   124         # If you delete a tag, the objects using the tag are unaffected
       
   125         # (other than losing a tag)
       
   126         tag = TaggedItem.objects.order_by("id")[0]
       
   127         tag.delete()
       
   128         self.assertQuerysetEqual(bacon.tags.all(), ["<TaggedItem: salty>"])
       
   129         self.assertQuerysetEqual(TaggedItem.objects.all(), [
       
   130                 (u'clearish', Mineral, quartz_pk),
       
   131                 (u'fatty', Animal, platypus.pk),
       
   132                 (u'salty', Vegetable, bacon.pk),
       
   133                 (u'shiny', Animal, platypus.pk)
       
   134             ],
       
   135             comp_func
       
   136         )
       
   137         TaggedItem.objects.filter(tag='fatty').delete()
       
   138         ctype = ContentType.objects.get_for_model(lion)
       
   139         self.assertQuerysetEqual(Animal.objects.filter(tags__content_type=ctype), [
       
   140             "<Animal: Platypus>"
       
   141         ])
       
   142 
       
   143 
       
   144     def test_multiple_gfk(self):
       
   145         # Simple tests for multiple GenericForeignKeys
       
   146         # only uses one model, since the above tests should be sufficient.
       
   147         tiger = Animal.objects.create(common_name="tiger")
       
   148         cheetah = Animal.objects.create(common_name="cheetah")
       
   149         bear = Animal.objects.create(common_name="bear")
       
   150 
       
   151         # Create directly
       
   152         Comparison.objects.create(
       
   153             first_obj=cheetah, other_obj=tiger, comparative="faster"
       
   154         )
       
   155         Comparison.objects.create(
       
   156             first_obj=tiger, other_obj=cheetah, comparative="cooler"
       
   157         )
       
   158 
       
   159         # Create using GenericRelation
       
   160         tiger.comparisons.create(other_obj=bear, comparative="cooler")
       
   161         tiger.comparisons.create(other_obj=cheetah, comparative="stronger")
       
   162         self.assertQuerysetEqual(cheetah.comparisons.all(), [
       
   163             "<Comparison: cheetah is faster than tiger>"
       
   164         ])
       
   165 
       
   166         # Filtering works
       
   167         self.assertQuerysetEqual(tiger.comparisons.filter(comparative="cooler"), [
       
   168             "<Comparison: tiger is cooler than cheetah>",
       
   169             "<Comparison: tiger is cooler than bear>"
       
   170         ])
       
   171 
       
   172         # Filtering and deleting works
       
   173         subjective = ["cooler"]
       
   174         tiger.comparisons.filter(comparative__in=subjective).delete()
       
   175         self.assertQuerysetEqual(Comparison.objects.all(), [
       
   176             "<Comparison: cheetah is faster than tiger>",
       
   177             "<Comparison: tiger is stronger than cheetah>"
       
   178         ])
       
   179 
       
   180         # If we delete cheetah, Comparisons with cheetah as 'first_obj' will be
       
   181         # deleted since Animal has an explicit GenericRelation to Comparison
       
   182         # through first_obj. Comparisons with cheetah as 'other_obj' will not
       
   183         # be deleted.
       
   184         cheetah.delete()
       
   185         self.assertQuerysetEqual(Comparison.objects.all(), [
       
   186             "<Comparison: tiger is stronger than None>"
       
   187         ])
       
   188 
       
   189     def test_gfk_subclasses(self):
       
   190         # GenericForeignKey should work with subclasses (see #8309)
       
   191         quartz = Mineral.objects.create(name="Quartz", hardness=7)
       
   192         valuedtag = ValuableTaggedItem.objects.create(
       
   193             content_object=quartz, tag="shiny", value=10
       
   194         )
       
   195         self.assertEqual(valuedtag.content_object, quartz)
       
   196 
       
   197     def test_generic_inline_formsets(self):
       
   198         GenericFormSet = generic_inlineformset_factory(TaggedItem, extra=1)
       
   199         formset = GenericFormSet()
       
   200         self.assertEqual(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>
       
   201 <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>""")
       
   202 
       
   203         formset = GenericFormSet(instance=Animal())
       
   204         self.assertEqual(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>
       
   205 <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 
       
   207         platypus = Animal.objects.create(
       
   208             common_name="Platypus", latin_name="Ornithorhynchus anatinus"
       
   209         )
       
   210         platypus.tags.create(tag="shiny")
       
   211         GenericFormSet = generic_inlineformset_factory(TaggedItem, extra=1)
       
   212         formset = GenericFormSet(instance=platypus)
       
   213         tagged_item_id = TaggedItem.objects.get(
       
   214             tag='shiny', object_id=platypus.id
       
   215         ).id
       
   216         self.assertEqual(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>
       
   217 <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>
       
   218 <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)
       
   219 
       
   220         lion = Animal.objects.create(common_name="Lion", latin_name="Panthera leo")
       
   221         formset = GenericFormSet(instance=lion, prefix='x')
       
   222         self.assertEqual(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>
       
   223 <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>""")