0
# Admin Interface
1
2
Django admin integration for managing tags and tagged items. Django-taggit provides comprehensive admin interfaces for tag management, including search functionality, tag merging capabilities, and inline editing of tagged items.
3
4
## Capabilities
5
6
### TagAdmin
7
8
Admin interface for managing Tag objects with search, filtering, and bulk operations.
9
10
```python { .api }
11
class TagAdmin(admin.ModelAdmin):
12
"""
13
Admin interface for Tag model.
14
15
Features:
16
- List display with name and slug
17
- Search functionality by tag name
18
- Alphabetical ordering
19
- Prepopulated slug field
20
- Tag merging action
21
"""
22
list_display = ["name", "slug"]
23
ordering = ["name", "slug"]
24
search_fields = ["name"]
25
prepopulated_fields = {"slug": ["name"]}
26
actions = ["render_tag_form"]
27
28
def get_urls(self):
29
"""Add custom URLs for tag management."""
30
31
def render_tag_form(self, request, queryset):
32
"""Admin action to merge selected tags."""
33
34
def merge_tags_view(self, request):
35
"""View for handling tag merge operations."""
36
```
37
38
```python
39
# Register the Tag admin (automatically done by django-taggit)
40
from django.contrib import admin
41
from taggit.models import Tag
42
from taggit.admin import TagAdmin
43
44
admin.site.register(Tag, TagAdmin)
45
```
46
47
### TaggedItemInline
48
49
Inline admin for managing TaggedItem relationships within other model admin interfaces.
50
51
```python { .api }
52
class TaggedItemInline(admin.StackedInline):
53
"""
54
Inline admin for TaggedItem model.
55
56
Allows editing tag relationships directly within
57
the admin interface of tagged models.
58
"""
59
model = TaggedItem
60
```
61
62
```python
63
from django.contrib import admin
64
from taggit.admin import TaggedItemInline
65
from myapp.models import Article
66
67
class ArticleAdmin(admin.ModelAdmin):
68
list_display = ['title', 'created_at']
69
inlines = [TaggedItemInline]
70
71
admin.site.register(Article, ArticleAdmin)
72
```
73
74
### Custom Model Admin Integration
75
76
Integrating tagging functionality into your model admin interfaces.
77
78
```python
79
from django.contrib import admin
80
from taggit.models import Tag
81
82
class ArticleAdmin(admin.ModelAdmin):
83
list_display = ['title', 'author', 'created_at', 'tag_list']
84
list_filter = ['created_at', 'tags']
85
search_fields = ['title', 'content', 'tags__name']
86
87
def tag_list(self, obj):
88
"""Display tags in list view."""
89
return ', '.join([tag.name for tag in obj.tags.all()])
90
tag_list.short_description = 'Tags'
91
92
def get_queryset(self, request):
93
"""Optimize queries for tag display."""
94
return super().get_queryset(request).prefetch_related('tags')
95
96
admin.site.register(Article, ArticleAdmin)
97
```
98
99
### Tag Merging
100
101
Built-in functionality for merging multiple tags into a single tag.
102
103
```python { .api }
104
class MergeTagsForm(forms.Form):
105
"""
106
Form for merging multiple tags into one.
107
108
Used in the admin interface to combine duplicate
109
or related tags into a single tag.
110
"""
111
new_tag_name = forms.CharField(
112
label="New Tag Name",
113
max_length=100,
114
help_text="Enter new or existing tag name"
115
)
116
```
117
118
The tag merging process:
119
120
1. Select multiple tags in the admin interface
121
2. Choose "Merge selected tags" action
122
3. Enter the target tag name
123
4. All tagged items are transferred to the target tag
124
5. Original tags are deleted (except the target if it already existed)
125
126
### Advanced Admin Customizations
127
128
Custom admin configurations for enhanced tag management.
129
130
```python
131
from django.contrib import admin
132
from django.db.models import Count
133
from taggit.models import Tag, TaggedItem
134
135
class TaggedItemAdmin(admin.ModelAdmin):
136
list_display = ['tag', 'content_type', 'object_id', 'content_object']
137
list_filter = ['content_type', 'tag']
138
search_fields = ['tag__name']
139
raw_id_fields = ['tag']
140
141
class CustomTagAdmin(admin.ModelAdmin):
142
list_display = ['name', 'slug', 'usage_count', 'created_at']
143
list_filter = ['created_at']
144
search_fields = ['name', 'slug']
145
readonly_fields = ['slug', 'usage_count']
146
ordering = ['-usage_count', 'name']
147
148
def usage_count(self, obj):
149
"""Show how many times tag is used."""
150
return obj.tagged_items.count()
151
usage_count.short_description = 'Usage Count'
152
usage_count.admin_order_field = 'tagged_items__count'
153
154
def get_queryset(self, request):
155
"""Annotate with usage count for sorting."""
156
return super().get_queryset(request).annotate(
157
usage_count=Count('tagged_items')
158
)
159
160
# Register custom admin
161
admin.site.unregister(Tag)
162
admin.site.register(Tag, CustomTagAdmin)
163
admin.site.register(TaggedItem, TaggedItemAdmin)
164
```
165
166
### Filtering and Search
167
168
Advanced filtering and search capabilities in the admin interface.
169
170
```python
171
from django.contrib import admin
172
from django.db.models import Q
173
174
class TagFilter(admin.SimpleListFilter):
175
title = 'Tag'
176
parameter_name = 'tag'
177
178
def lookups(self, request, model_admin):
179
"""Provide tag options for filtering."""
180
tags = Tag.objects.all().order_by('name')[:50] # Limit for performance
181
return [(tag.id, tag.name) for tag in tags]
182
183
def queryset(self, request, queryset):
184
"""Filter queryset by selected tag."""
185
if self.value():
186
return queryset.filter(tags__id=self.value())
187
return queryset
188
189
class PopularTagFilter(admin.SimpleListFilter):
190
title = 'Tag Popularity'
191
parameter_name = 'popularity'
192
193
def lookups(self, request, model_admin):
194
return [
195
('popular', 'Popular (10+ uses)'),
196
('moderate', 'Moderate (5-9 uses)'),
197
('rare', 'Rare (1-4 uses)'),
198
('unused', 'Unused'),
199
]
200
201
def queryset(self, request, queryset):
202
from django.db.models import Count
203
204
if self.value() == 'popular':
205
return queryset.annotate(
206
usage=Count('tagged_items')
207
).filter(usage__gte=10)
208
elif self.value() == 'moderate':
209
return queryset.annotate(
210
usage=Count('tagged_items')
211
).filter(usage__range=(5, 9))
212
elif self.value() == 'rare':
213
return queryset.annotate(
214
usage=Count('tagged_items')
215
).filter(usage__range=(1, 4))
216
elif self.value() == 'unused':
217
return queryset.filter(tagged_items__isnull=True)
218
return queryset
219
220
class ArticleAdmin(admin.ModelAdmin):
221
list_display = ['title', 'author', 'tag_list']
222
list_filter = [TagFilter, 'created_at']
223
search_fields = ['title', 'content', 'tags__name']
224
225
def tag_list(self, obj):
226
return ', '.join([tag.name for tag in obj.tags.all()[:3]])
227
tag_list.short_description = 'Tags'
228
```
229
230
### Bulk Operations
231
232
Implementing bulk operations for tag management in the admin.
233
234
```python
235
from django.contrib import admin, messages
236
from django.shortcuts import redirect
237
238
class BulkTagAdmin(admin.ModelAdmin):
239
actions = ['add_tag_to_selected', 'remove_tag_from_selected', 'clear_tags_from_selected']
240
241
def add_tag_to_selected(self, request, queryset):
242
"""Add a specific tag to selected objects."""
243
tag_name = "bulk-processed" # Could be made configurable
244
count = 0
245
for obj in queryset:
246
obj.tags.add(tag_name)
247
count += 1
248
messages.success(request, f'Added tag "{tag_name}" to {count} objects.')
249
add_tag_to_selected.short_description = 'Add "bulk-processed" tag to selected items'
250
251
def remove_tag_from_selected(self, request, queryset):
252
"""Remove a specific tag from selected objects."""
253
tag_name = "draft"
254
count = 0
255
for obj in queryset:
256
obj.tags.remove(tag_name)
257
count += 1
258
messages.success(request, f'Removed tag "{tag_name}" from {count} objects.')
259
remove_tag_from_selected.short_description = 'Remove "draft" tag from selected items'
260
261
def clear_tags_from_selected(self, request, queryset):
262
"""Clear all tags from selected objects."""
263
count = 0
264
for obj in queryset:
265
obj.tags.clear()
266
count += 1
267
messages.success(request, f'Cleared all tags from {count} objects.')
268
clear_tags_from_selected.short_description = 'Clear all tags from selected items'
269
270
class ArticleAdmin(BulkTagAdmin):
271
list_display = ['title', 'author', 'tag_list']
272
# ... other configurations
273
```
274
275
### Custom Admin Templates
276
277
Customizing admin templates for enhanced tag management interfaces.
278
279
```html
280
<!-- admin/taggit/tag/change_list.html -->
281
{% extends "admin/change_list.html" %}
282
283
{% block extrahead %}
284
{{ block.super }}
285
<style>
286
.tag-usage-high { background-color: #d4edda; }
287
.tag-usage-medium { background-color: #fff3cd; }
288
.tag-usage-low { background-color: #f8d7da; }
289
</style>
290
{% endblock %}
291
292
{% block result_list %}
293
<script>
294
// Add visual indicators for tag usage
295
document.addEventListener('DOMContentLoaded', function() {
296
const rows = document.querySelectorAll('tbody tr');
297
rows.forEach(function(row) {
298
const usageCell = row.querySelector('td:nth-child(3)');
299
if (usageCell) {
300
const usage = parseInt(usageCell.textContent);
301
if (usage >= 10) {
302
row.classList.add('tag-usage-high');
303
} else if (usage >= 5) {
304
row.classList.add('tag-usage-medium');
305
} else if (usage > 0) {
306
row.classList.add('tag-usage-low');
307
}
308
}
309
});
310
});
311
</script>
312
{{ block.super }}
313
{% endblock %}
314
```
315
316
### Tag Statistics Dashboard
317
318
Creating a custom admin view for tag statistics and management.
319
320
```python
321
from django.contrib import admin
322
from django.urls import path
323
from django.shortcuts import render
324
from django.db.models import Count
325
from taggit.models import Tag
326
327
class TagStatsAdmin(admin.ModelAdmin):
328
change_list_template = 'admin/tag_stats.html'
329
330
def changelist_view(self, request, extra_context=None):
331
# Tag statistics
332
tag_stats = Tag.objects.annotate(
333
usage_count=Count('tagged_items')
334
).order_by('-usage_count')
335
336
popular_tags = tag_stats[:10]
337
unused_tags = tag_stats.filter(usage_count=0)
338
339
extra_context = extra_context or {}
340
extra_context.update({
341
'popular_tags': popular_tags,
342
'unused_tags_count': unused_tags.count(),
343
'total_tags': tag_stats.count(),
344
})
345
346
return super().changelist_view(request, extra_context=extra_context)
347
348
# Register with custom admin
349
admin.site.unregister(Tag)
350
admin.site.register(Tag, TagStatsAdmin)
351
```
352
353
```html
354
<!-- templates/admin/tag_stats.html -->
355
{% extends "admin/change_list.html" %}
356
357
{% block content_title %}
358
<h1>Tag Statistics</h1>
359
{% endblock %}
360
361
{% block result_list %}
362
<div class="results">
363
<div class="stats-summary">
364
<h2>Summary</h2>
365
<p>Total Tags: {{ total_tags }}</p>
366
<p>Unused Tags: {{ unused_tags_count }}</p>
367
</div>
368
369
<div class="popular-tags">
370
<h2>Most Popular Tags</h2>
371
<table>
372
<thead>
373
<tr>
374
<th>Tag</th>
375
<th>Usage Count</th>
376
</tr>
377
</thead>
378
<tbody>
379
{% for tag in popular_tags %}
380
<tr>
381
<td><a href="{% url 'admin:taggit_tag_change' tag.pk %}">{{ tag.name }}</a></td>
382
<td>{{ tag.usage_count }}</td>
383
</tr>
384
{% endfor %}
385
</tbody>
386
</table>
387
</div>
388
</div>
389
390
{{ block.super }}
391
{% endblock %}
392
```
393
394
### Permissions and Security
395
396
Managing permissions for tag administration.
397
398
```python
399
from django.contrib import admin
400
from django.contrib.auth.decorators import permission_required
401
402
class SecureTagAdmin(admin.ModelAdmin):
403
def has_delete_permission(self, request, obj=None):
404
"""Restrict tag deletion to superusers."""
405
return request.user.is_superuser
406
407
def has_change_permission(self, request, obj=None):
408
"""Allow tag editing for users with change permission."""
409
return request.user.has_perm('taggit.change_tag')
410
411
def get_queryset(self, request):
412
"""Filter tags based on user permissions."""
413
qs = super().get_queryset(request)
414
if not request.user.is_superuser:
415
# Regular users can only see tags they've created
416
# (requires custom Tag model with created_by field)
417
pass
418
return qs
419
420
# Custom permissions in models.py
421
class CustomTag(Tag):
422
created_by = models.ForeignKey('auth.User', on_delete=models.CASCADE)
423
424
class Meta:
425
permissions = [
426
('can_merge_tags', 'Can merge tags'),
427
('can_bulk_delete_tags', 'Can bulk delete tags'),
428
]
429
```