1import uuid 2 3from django.db import models 4 5from taggit.managers import TaggableManager 6from taggit.models import ( 7 CommonGenericTaggedItemBase, 8 GenericTaggedItemBase, 9 GenericUUIDTaggedItemBase, 10 ItemBase, 11 Tag, 12 TagBase, 13 TaggedItem, 14 TaggedItemBase, 15) 16 17 18# base test model 19class TestModel(models.Model): 20 tags = TaggableManager() 21 22 23# Ensure that two TaggableManagers with custom through model are allowed. 24class Through1(TaggedItemBase): 25 content_object = models.ForeignKey("MultipleTags", on_delete=models.CASCADE) 26 27 28class Through2(TaggedItemBase): 29 content_object = models.ForeignKey("MultipleTags", on_delete=models.CASCADE) 30 31 32class MultipleTags(models.Model): 33 tags1 = TaggableManager(through=Through1, related_name="tags1") 34 tags2 = TaggableManager(through=Through2, related_name="tags2") 35 36 37# Ensure that two TaggableManagers with GFK via different through models are allowed. 38class ThroughGFK(GenericTaggedItemBase): 39 tag = models.ForeignKey(Tag, related_name="tagged_items", on_delete=models.CASCADE) 40 41 42class MultipleTagsGFK(models.Model): 43 tags1 = TaggableManager(related_name="tagsgfk1") 44 tags2 = TaggableManager(through=ThroughGFK, related_name="tagsgfk2") 45 46 47class BlankTagModel(models.Model): 48 name = models.CharField(max_length=50) 49 50 tags = TaggableManager(blank=True) 51 52 def __str__(self): 53 return self.name 54 55 56class Food(models.Model): 57 name = models.CharField(max_length=50) 58 59 tags = TaggableManager() 60 61 def __str__(self): 62 return self.name 63 64 65class BaseFood(models.Model): 66 name = models.CharField(max_length=50) 67 68 def __str__(self): 69 return self.name 70 71 72class MultiInheritanceLazyResolutionFoodTag(TaggedItemBase): 73 content_object = models.ForeignKey( 74 "MultiInheritanceFood", related_name="tagged_items", on_delete=models.CASCADE 75 ) 76 77 class Meta: 78 unique_together = [["content_object", "tag"]] 79 80 81class MultiInheritanceFood(BaseFood): 82 tags = TaggableManager(through=MultiInheritanceLazyResolutionFoodTag) 83 84 def __str__(self): 85 return self.name 86 87 88class Pet(models.Model): 89 name = models.CharField(max_length=50) 90 91 tags = TaggableManager() 92 93 def __str__(self): 94 return self.name 95 96 97class HousePet(Pet): 98 trained = models.BooleanField(default=False) 99 100 101# Test direct-tagging with custom through model 102 103 104class TaggedFood(TaggedItemBase): 105 content_object = models.ForeignKey("DirectFood", on_delete=models.CASCADE) 106 107 class Meta: 108 unique_together = [["content_object", "tag"]] 109 110 111class TaggedPet(TaggedItemBase): 112 content_object = models.ForeignKey("DirectPet", on_delete=models.CASCADE) 113 114 115class DirectFood(models.Model): 116 name = models.CharField(max_length=50) 117 118 tags = TaggableManager(through="TaggedFood") 119 120 def __str__(self): 121 return self.name 122 123 124class DirectPet(models.Model): 125 name = models.CharField(max_length=50) 126 127 tags = TaggableManager(through=TaggedPet) 128 129 def __str__(self): 130 return self.name 131 132 133class DirectHousePet(DirectPet): 134 trained = models.BooleanField(default=False) 135 136 137# Test direct-tagging with custom through model and custom tag 138 139 140class TrackedTag(TagBase): 141 created_by = models.CharField(max_length=50) 142 created_dt = models.DateTimeField(auto_now_add=True) 143 description = models.TextField(blank=True, max_length=255, null=True) 144 145 146class TaggedTrackedFood(ItemBase): 147 content_object = models.ForeignKey("DirectTrackedFood", on_delete=models.CASCADE) 148 tag = models.ForeignKey( 149 TrackedTag, on_delete=models.CASCADE, related_name="%(class)s_items" 150 ) 151 created_by = models.CharField(max_length=50) 152 created_dt = models.DateTimeField(auto_now_add=True) 153 154 class Meta: 155 unique_together = ["content_object", "tag"] 156 157 158class TaggedTrackedPet(ItemBase): 159 content_object = models.ForeignKey("DirectTrackedPet", on_delete=models.CASCADE) 160 tag = models.ForeignKey( 161 TrackedTag, on_delete=models.CASCADE, related_name="%(class)s_items" 162 ) 163 created_by = models.CharField(max_length=50) 164 created_dt = models.DateTimeField(auto_now_add=True) 165 166 167class DirectTrackedFood(models.Model): 168 name = models.CharField(max_length=50) 169 170 tags = TaggableManager(through=TaggedTrackedFood) 171 172 def __str__(self): 173 return self.name 174 175 176class DirectTrackedPet(models.Model): 177 name = models.CharField(max_length=50) 178 179 tags = TaggableManager(through=TaggedTrackedPet) 180 181 def __str__(self): 182 return self.name 183 184 185class DirectTrackedHousePet(DirectTrackedPet): 186 trained = models.BooleanField(default=False) 187 188 189# Test custom through model to model with custom PK 190 191 192class TaggedCustomPKFood(TaggedItemBase): 193 content_object = models.ForeignKey("DirectCustomPKFood", on_delete=models.CASCADE) 194 195 class Meta: 196 unique_together = [["content_object", "tag"]] 197 198 199class TaggedCustomPKPet(TaggedItemBase): 200 content_object = models.ForeignKey("DirectCustomPKPet", on_delete=models.CASCADE) 201 202 class Meta: 203 unique_together = [["content_object", "tag"]] 204 205 206class DirectCustomPKFood(models.Model): 207 name = models.CharField(max_length=50, primary_key=True) 208 tags = TaggableManager(through=TaggedCustomPKFood) 209 210 def __str__(self): 211 return self.name 212 213 214class DirectCustomPKPet(models.Model): 215 name = models.CharField(max_length=50, primary_key=True) 216 217 tags = TaggableManager(through=TaggedCustomPKPet) 218 219 def __str__(self): 220 return self.name 221 222 223class DirectCustomPKHousePet(DirectCustomPKPet): 224 trained = models.BooleanField(default=False) 225 226 227# Test custom through model to model with custom PK using GenericForeignKey 228class TaggedCustomPK(CommonGenericTaggedItemBase, TaggedItemBase): 229 object_id = models.CharField(max_length=50, verbose_name="Object id", db_index=True) 230 231 class Meta: 232 unique_together = [["object_id", "tag"]] 233 234 235class CustomPKFood(models.Model): 236 name = models.CharField(max_length=50, primary_key=True) 237 238 tags = TaggableManager(through=TaggedCustomPK) 239 240 def __str__(self): 241 return self.name 242 243 244class CustomPKPet(models.Model): 245 name = models.CharField(max_length=50, primary_key=True) 246 247 tags = TaggableManager(through=TaggedCustomPK) 248 249 def __str__(self): 250 return self.name 251 252 253class CustomPKHousePet(CustomPKPet): 254 trained = models.BooleanField(default=False) 255 256 257# Test custom through model to a custom tag model 258 259 260class OfficialTag(TagBase): 261 official = models.BooleanField(default=False) 262 263 264class OfficialThroughModel(GenericTaggedItemBase): 265 tag = models.ForeignKey( 266 OfficialTag, related_name="tagged_items", on_delete=models.CASCADE 267 ) 268 extra_field = models.CharField(max_length=10) 269 270 class Meta: 271 unique_together = [["content_type", "object_id", "tag"]] 272 273 274class OfficialFood(models.Model): 275 name = models.CharField(max_length=50) 276 277 tags = TaggableManager(through=OfficialThroughModel) 278 279 def __str__(self): 280 return self.name 281 282 283class OfficialPet(models.Model): 284 name = models.CharField(max_length=50) 285 286 tags = TaggableManager(through=OfficialThroughModel) 287 288 def __str__(self): 289 return self.name 290 291 292class OfficialHousePet(OfficialPet): 293 trained = models.BooleanField(default=False) 294 295 296class Media(models.Model): 297 tags = TaggableManager() 298 299 class Meta: 300 abstract = True 301 302 303class Photo(Media): 304 pass 305 306 307class Movie(Media): 308 pass 309 310 311class ProxyPhoto(Photo): 312 class Meta: 313 proxy = True 314 315 316class ArticleTag(Tag): 317 class Meta: 318 proxy = True 319 320 def slugify(self, tag, i=None): 321 slug = "category-%s" % tag.lower() 322 323 if i is not None: 324 slug += "-%d" % i 325 return slug 326 327 328class ArticleTaggedItem(TaggedItem): 329 class Meta: 330 proxy = True 331 332 @classmethod 333 def tag_model(self): 334 return ArticleTag 335 336 337class Article(models.Model): 338 title = models.CharField(max_length=100) 339 340 tags = TaggableManager(through=ArticleTaggedItem) 341 342 343class CustomManager(models.Model): 344 class Foo: 345 def __init__(*args, **kwargs): 346 pass 347 348 tags = TaggableManager(manager=Foo) 349 350 351class Parent(models.Model): 352 tags = TaggableManager() 353 354 355class Child(Parent): 356 pass 357 358 359class UUIDFood(models.Model): 360 id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) 361 name = models.CharField(max_length=50) 362 tags = TaggableManager(through="UUIDTaggedItem") 363 364 created_at = models.DateTimeField(auto_now_add=True) 365 366 def __str__(self): 367 return self.name 368 369 class Meta: 370 # With a UUIDField pk, objects are not always ordered by creation time. So explicitly set ordering. 371 ordering = ["created_at"] 372 373 374class UUIDPet(models.Model): 375 id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) 376 name = models.CharField(max_length=50) 377 378 tags = TaggableManager(through="UUIDTaggedItem") 379 created_at = models.DateTimeField(auto_now_add=True) 380 381 def __str__(self): 382 return self.name 383 384 class Meta: 385 # With a UUIDField pk, objects are not always ordered by creation time. So explicitly set ordering. 386 ordering = ["created_at"] 387 388 389class UUIDHousePet(UUIDPet): 390 trained = models.BooleanField(default=False) 391 392 393class UUIDTag(TagBase): 394 id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) 395 396 397class UUIDTaggedItem(GenericUUIDTaggedItemBase): 398 tag = models.ForeignKey( 399 UUIDTag, related_name="%(app_label)s_%(class)s_items", on_delete=models.CASCADE 400 ) 401 402 class Meta: 403 unique_together = [["content_type", "object_id", "tag"]] 404 405 406# Exists to verify system check failure. 407# tests.Name.tags: (fields.E303) Reverse query name for 'Name.tags' clashes with field name 'Tag.name'. 408# HINT: Rename field 'Tag.name', or add/change a related_name argument to the definition for field 'Name.tags'. 409class Name(models.Model): 410 tags = TaggableManager(related_name="a_unique_related_name") 411