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