1. """
    
  2. Generic relations
    
  3. 
    
  4. Generic relations let an object have a foreign key to any object through a
    
  5. content-type/object-id field. A ``GenericForeignKey`` field can point to any
    
  6. object, be it animal, vegetable, or mineral.
    
  7. 
    
  8. The canonical example is tags (although this example implementation is *far*
    
  9. from complete).
    
  10. """
    
  11. 
    
  12. from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
    
  13. from django.contrib.contenttypes.models import ContentType
    
  14. from django.db import models
    
  15. 
    
  16. 
    
  17. class TaggedItem(models.Model):
    
  18.     """A tag on an item."""
    
  19. 
    
  20.     tag = models.SlugField()
    
  21.     content_type = models.ForeignKey(ContentType, models.CASCADE)
    
  22.     object_id = models.PositiveIntegerField()
    
  23. 
    
  24.     content_object = GenericForeignKey()
    
  25. 
    
  26.     class Meta:
    
  27.         ordering = ["tag", "content_type__model"]
    
  28. 
    
  29.     def __str__(self):
    
  30.         return self.tag
    
  31. 
    
  32. 
    
  33. class ValuableTaggedItem(TaggedItem):
    
  34.     value = models.PositiveIntegerField()
    
  35. 
    
  36. 
    
  37. class AbstractComparison(models.Model):
    
  38.     comparative = models.CharField(max_length=50)
    
  39. 
    
  40.     content_type1 = models.ForeignKey(
    
  41.         ContentType, models.CASCADE, related_name="comparative1_set"
    
  42.     )
    
  43.     object_id1 = models.PositiveIntegerField()
    
  44. 
    
  45.     first_obj = GenericForeignKey(ct_field="content_type1", fk_field="object_id1")
    
  46. 
    
  47. 
    
  48. class Comparison(AbstractComparison):
    
  49.     """
    
  50.     A model that tests having multiple GenericForeignKeys. One is defined
    
  51.     through an inherited abstract model and one defined directly on this class.
    
  52.     """
    
  53. 
    
  54.     content_type2 = models.ForeignKey(
    
  55.         ContentType, models.CASCADE, related_name="comparative2_set"
    
  56.     )
    
  57.     object_id2 = models.PositiveIntegerField()
    
  58. 
    
  59.     other_obj = GenericForeignKey(ct_field="content_type2", fk_field="object_id2")
    
  60. 
    
  61.     def __str__(self):
    
  62.         return "%s is %s than %s" % (self.first_obj, self.comparative, self.other_obj)
    
  63. 
    
  64. 
    
  65. class Animal(models.Model):
    
  66.     common_name = models.CharField(max_length=150)
    
  67.     latin_name = models.CharField(max_length=150)
    
  68. 
    
  69.     tags = GenericRelation(TaggedItem, related_query_name="animal")
    
  70.     comparisons = GenericRelation(
    
  71.         Comparison, object_id_field="object_id1", content_type_field="content_type1"
    
  72.     )
    
  73. 
    
  74.     def __str__(self):
    
  75.         return self.common_name
    
  76. 
    
  77. 
    
  78. class Vegetable(models.Model):
    
  79.     name = models.CharField(max_length=150)
    
  80.     is_yucky = models.BooleanField(default=True)
    
  81. 
    
  82.     tags = GenericRelation(TaggedItem)
    
  83. 
    
  84.     def __str__(self):
    
  85.         return self.name
    
  86. 
    
  87. 
    
  88. class Carrot(Vegetable):
    
  89.     pass
    
  90. 
    
  91. 
    
  92. class Mineral(models.Model):
    
  93.     name = models.CharField(max_length=150)
    
  94.     hardness = models.PositiveSmallIntegerField()
    
  95. 
    
  96.     # note the lack of an explicit GenericRelation here...
    
  97. 
    
  98.     def __str__(self):
    
  99.         return self.name
    
  100. 
    
  101. 
    
  102. class GeckoManager(models.Manager):
    
  103.     def get_queryset(self):
    
  104.         return super().get_queryset().filter(has_tail=True)
    
  105. 
    
  106. 
    
  107. class Gecko(models.Model):
    
  108.     has_tail = models.BooleanField(default=False)
    
  109.     objects = GeckoManager()
    
  110. 
    
  111. 
    
  112. # To test fix for #11263
    
  113. class Rock(Mineral):
    
  114.     tags = GenericRelation(TaggedItem)
    
  115. 
    
  116. 
    
  117. class ValuableRock(Mineral):
    
  118.     tags = GenericRelation(ValuableTaggedItem)
    
  119. 
    
  120. 
    
  121. class ManualPK(models.Model):
    
  122.     id = models.IntegerField(primary_key=True)
    
  123.     tags = GenericRelation(TaggedItem, related_query_name="manualpk")
    
  124. 
    
  125. 
    
  126. class ForProxyModelModel(models.Model):
    
  127.     content_type = models.ForeignKey(ContentType, models.CASCADE)
    
  128.     object_id = models.PositiveIntegerField()
    
  129.     obj = GenericForeignKey(for_concrete_model=False)
    
  130.     title = models.CharField(max_length=255, null=True)
    
  131. 
    
  132. 
    
  133. class ForConcreteModelModel(models.Model):
    
  134.     content_type = models.ForeignKey(ContentType, models.CASCADE)
    
  135.     object_id = models.PositiveIntegerField()
    
  136.     obj = GenericForeignKey()
    
  137. 
    
  138. 
    
  139. class ConcreteRelatedModel(models.Model):
    
  140.     bases = GenericRelation(ForProxyModelModel, for_concrete_model=False)
    
  141. 
    
  142. 
    
  143. class ProxyRelatedModel(ConcreteRelatedModel):
    
  144.     class Meta:
    
  145.         proxy = True
    
  146. 
    
  147. 
    
  148. # To test fix for #7551
    
  149. class AllowsNullGFK(models.Model):
    
  150.     content_type = models.ForeignKey(ContentType, models.SET_NULL, null=True)
    
  151.     object_id = models.PositiveIntegerField(null=True)
    
  152.     content_object = GenericForeignKey()