0
# Form Integration
1
2
Django forms integration for handling tag input and validation. Django-taggit provides form fields and widgets that make it easy to include tag functionality in Django forms with proper validation and user-friendly input methods.
3
4
## Capabilities
5
6
### TagField
7
8
A Django form field specifically designed for handling tag input as comma-separated values.
9
10
```python { .api }
11
class TagField(forms.CharField):
12
"""
13
Form field for handling tag input.
14
15
Inherits from CharField and provides tag-specific validation
16
and parsing of comma/space-separated tag input.
17
"""
18
widget = TagWidget
19
20
def clean(self, value):
21
"""
22
Clean and validate tag input.
23
24
Parameters:
25
- value (str): Raw input string from form
26
27
Returns:
28
list: Parsed list of tag names
29
30
Raises:
31
ValidationError: If input format is invalid
32
"""
33
34
def has_changed(self, initial_value, data_value):
35
"""
36
Check if field value has changed.
37
38
Parameters:
39
- initial_value: Original tag list
40
- data_value: New form input
41
42
Returns:
43
bool: True if tags have changed
44
"""
45
```
46
47
```python
48
from django import forms
49
from taggit.forms import TagField
50
51
class ArticleForm(forms.Form):
52
title = forms.CharField(max_length=200)
53
content = forms.CharField(widget=forms.Textarea)
54
tags = TagField()
55
56
# Usage in views
57
form = ArticleForm(data={'title': 'My Article', 'tags': 'python, django, tutorial'})
58
if form.is_valid():
59
tags = form.cleaned_data['tags'] # ['python', 'django', 'tutorial']
60
```
61
62
### Tag Widgets
63
64
Widget classes for rendering tag input fields in forms with appropriate formatting.
65
66
```python { .api }
67
class TagWidget(forms.TextInput):
68
"""
69
Text input widget for tag input.
70
71
Provides formatting for tag display in forms, converting
72
tag objects to comma-separated strings for editing.
73
"""
74
75
class TextareaTagWidget(forms.Textarea):
76
"""
77
Textarea widget for tag input.
78
79
Useful for cases where many tags are expected or
80
longer tag names are common.
81
"""
82
83
class TagWidgetMixin:
84
"""
85
Mixin providing tag formatting functionality.
86
87
Can be used with any form widget to add tag formatting.
88
"""
89
def format_value(self, value):
90
"""
91
Format tag objects for display in forms.
92
93
Parameters:
94
- value: Tag objects or string value
95
96
Returns:
97
str: Formatted tag string for form display
98
"""
99
```
100
101
### Admin Form Classes
102
103
Form classes specifically designed for admin interface functionality.
104
105
```python { .api }
106
class MergeTagsForm(forms.Form):
107
"""
108
Form for merging multiple tags into one.
109
110
Used in the admin interface to combine duplicate
111
or related tags into a single tag.
112
"""
113
new_tag_name = forms.CharField(
114
label="New Tag Name",
115
max_length=100,
116
help_text="Enter the name for the merged tag"
117
)
118
119
def clean_new_tag_name(self):
120
"""
121
Validate the new tag name.
122
123
Returns:
124
str: Cleaned tag name
125
"""
126
```
127
128
```python
129
from taggit.forms import TagField, TagWidget, TextareaTagWidget
130
131
class ArticleForm(forms.Form):
132
title = forms.CharField(max_length=200)
133
134
# Default text input widget
135
tags = TagField()
136
137
# Custom textarea widget for longer tag lists
138
keywords = TagField(widget=TextareaTagWidget(attrs={'rows': 3}))
139
140
# Custom styling
141
categories = TagField(
142
widget=TagWidget(attrs={
143
'class': 'form-control',
144
'placeholder': 'Enter tags separated by commas'
145
})
146
)
147
```
148
149
### ModelForm Integration
150
151
Integration with Django ModelForms for seamless tag handling in model forms.
152
153
```python
154
from django import forms
155
from taggit.forms import TagField
156
from myapp.models import Article
157
158
class ArticleModelForm(forms.ModelForm):
159
class Meta:
160
model = Article
161
fields = ['title', 'content', 'tags']
162
163
# TaggableManager fields are automatically handled
164
# but can be customized if needed
165
tags = TagField(
166
required=False,
167
help_text="Enter tags separated by commas"
168
)
169
```
170
171
### Custom Form Field Validation
172
173
Advanced validation patterns for tag input fields.
174
175
```python
176
from django import forms
177
from django.core.exceptions import ValidationError
178
from taggit.forms import TagField
179
180
class CustomTagField(TagField):
181
def clean(self, value):
182
tags = super().clean(value)
183
184
# Custom validation: limit number of tags
185
if len(tags) > 10:
186
raise ValidationError("Maximum 10 tags allowed")
187
188
# Custom validation: tag length
189
for tag in tags:
190
if len(tag) > 50:
191
raise ValidationError(f"Tag '{tag}' is too long (max 50 characters)")
192
193
# Custom validation: forbidden words
194
forbidden = ['spam', 'inappropriate']
195
for tag in tags:
196
if tag.lower() in forbidden:
197
raise ValidationError(f"Tag '{tag}' is not allowed")
198
199
return tags
200
201
class ArticleForm(forms.Form):
202
title = forms.CharField(max_length=200)
203
tags = CustomTagField()
204
```
205
206
### Form Processing
207
208
Complete examples of processing forms with tag fields.
209
210
```python
211
from django.shortcuts import render, redirect
212
from django.contrib import messages
213
from myapp.models import Article
214
from myapp.forms import ArticleForm
215
216
def create_article(request):
217
if request.method == 'POST':
218
form = ArticleForm(request.POST)
219
if form.is_valid():
220
# Create article
221
article = Article.objects.create(
222
title=form.cleaned_data['title'],
223
content=form.cleaned_data['content']
224
)
225
226
# Add tags
227
article.tags.set(form.cleaned_data['tags'])
228
229
messages.success(request, 'Article created successfully!')
230
return redirect('article_detail', pk=article.pk)
231
else:
232
form = ArticleForm()
233
234
return render(request, 'articles/create.html', {'form': form})
235
236
def edit_article(request, pk):
237
article = Article.objects.get(pk=pk)
238
239
if request.method == 'POST':
240
form = ArticleForm(request.POST)
241
if form.is_valid():
242
article.title = form.cleaned_data['title']
243
article.content = form.cleaned_data['content']
244
article.save()
245
246
# Update tags
247
article.tags.set(form.cleaned_data['tags'])
248
249
return redirect('article_detail', pk=article.pk)
250
else:
251
# Pre-populate form with existing tags
252
form = ArticleForm(initial={
253
'title': article.title,
254
'content': article.content,
255
'tags': ', '.join(article.tags.names())
256
})
257
258
return render(request, 'articles/edit.html', {'form': form, 'article': article})
259
```
260
261
### AJAX and Dynamic Forms
262
263
Implementing dynamic tag functionality with JavaScript and AJAX.
264
265
```python
266
from django.http import JsonResponse
267
from django.views.decorators.http import require_GET
268
from taggit.models import Tag
269
270
@require_GET
271
def tag_autocomplete(request):
272
"""
273
Provide tag suggestions for autocomplete functionality.
274
"""
275
term = request.GET.get('term', '')
276
if len(term) < 2:
277
return JsonResponse({'suggestions': []})
278
279
tags = Tag.objects.filter(
280
name__icontains=term
281
).values_list('name', flat=True)[:10]
282
283
return JsonResponse({'suggestions': list(tags)})
284
285
# In template (example JavaScript for autocomplete)
286
"""
287
<script>
288
$('#id_tags').autocomplete({
289
source: "{% url 'tag_autocomplete' %}",
290
minLength: 2
291
});
292
</script>
293
"""
294
```
295
296
### Tag Input Parsing
297
298
Understanding how django-taggit parses different tag input formats.
299
300
```python
301
from taggit.utils import parse_tags
302
303
# Different input formats and their parsed results
304
examples = [
305
('python, django, tutorial', ['django', 'python', 'tutorial']),
306
('python django tutorial', ['django', 'python', 'tutorial']),
307
('"web development", python, django', ['django', 'python', 'web development']),
308
('python,django,tutorial', ['django', 'python', 'tutorial']),
309
('"multi word tag", single', ['multi word tag', 'single']),
310
]
311
312
for input_str, expected in examples:
313
result = parse_tags(input_str)
314
print(f"Input: {input_str}")
315
print(f"Parsed: {result}")
316
print(f"Expected: {expected}")
317
print("---")
318
```
319
320
### Form Rendering and Templates
321
322
Template examples for rendering tag forms with proper styling.
323
324
```html
325
<!-- Basic form rendering -->
326
<form method="post">
327
{% csrf_token %}
328
{{ form.as_p }}
329
<button type="submit">Save</button>
330
</form>
331
332
<!-- Custom form rendering -->
333
<form method="post">
334
{% csrf_token %}
335
<div class="form-group">
336
<label for="{{ form.title.id_for_label }}">Title:</label>
337
{{ form.title }}
338
{% if form.title.errors %}
339
<div class="error">{{ form.title.errors }}</div>
340
{% endif %}
341
</div>
342
343
<div class="form-group">
344
<label for="{{ form.tags.id_for_label }}">Tags:</label>
345
{{ form.tags }}
346
<small class="help-text">{{ form.tags.help_text }}</small>
347
{% if form.tags.errors %}
348
<div class="error">{{ form.tags.errors }}</div>
349
{% endif %}
350
</div>
351
352
<button type="submit" class="btn btn-primary">Save Article</button>
353
</form>
354
355
<!-- With Bootstrap styling -->
356
<div class="mb-3">
357
<label for="{{ form.tags.id_for_label }}" class="form-label">Tags</label>
358
<input type="{{ form.tags.widget.input_type }}"
359
name="{{ form.tags.name }}"
360
value="{{ form.tags.value|default:'' }}"
361
class="form-control{% if form.tags.errors %} is-invalid{% endif %}"
362
id="{{ form.tags.id_for_label }}"
363
placeholder="Enter tags separated by commas">
364
{% if form.tags.help_text %}
365
<div class="form-text">{{ form.tags.help_text }}</div>
366
{% endif %}
367
{% if form.tags.errors %}
368
<div class="invalid-feedback">
369
{% for error in form.tags.errors %}{{ error }}{% endfor %}
370
</div>
371
{% endif %}
372
</div>
373
```
374
375
### Formsets and Inline Forms
376
377
Using tag fields in Django formsets and inline forms.
378
379
```python
380
from django.forms import modelformset_factory
381
from myapp.models import Article
382
383
# Create formset with tag support
384
ArticleFormSet = modelformset_factory(
385
Article,
386
fields=['title', 'content', 'tags'],
387
extra=2
388
)
389
390
def bulk_edit_articles(request):
391
queryset = Article.objects.filter(author=request.user)
392
393
if request.method == 'POST':
394
formset = ArticleFormSet(request.POST, queryset=queryset)
395
if formset.is_valid():
396
formset.save()
397
return redirect('article_list')
398
else:
399
formset = ArticleFormSet(queryset=queryset)
400
401
return render(request, 'articles/bulk_edit.html', {'formset': formset})
402
```