A reusable Django application for simple tagging with comprehensive manager and form support.
—
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.
A Django form field specifically designed for handling tag input as comma-separated values.
class TagField(forms.CharField):
"""
Form field for handling tag input.
Inherits from CharField and provides tag-specific validation
and parsing of comma/space-separated tag input.
"""
widget = TagWidget
def clean(self, value):
"""
Clean and validate tag input.
Parameters:
- value (str): Raw input string from form
Returns:
list: Parsed list of tag names
Raises:
ValidationError: If input format is invalid
"""
def has_changed(self, initial_value, data_value):
"""
Check if field value has changed.
Parameters:
- initial_value: Original tag list
- data_value: New form input
Returns:
bool: True if tags have changed
"""from django import forms
from taggit.forms import TagField
class ArticleForm(forms.Form):
title = forms.CharField(max_length=200)
content = forms.CharField(widget=forms.Textarea)
tags = TagField()
# Usage in views
form = ArticleForm(data={'title': 'My Article', 'tags': 'python, django, tutorial'})
if form.is_valid():
tags = form.cleaned_data['tags'] # ['python', 'django', 'tutorial']Widget classes for rendering tag input fields in forms with appropriate formatting.
class TagWidget(forms.TextInput):
"""
Text input widget for tag input.
Provides formatting for tag display in forms, converting
tag objects to comma-separated strings for editing.
"""
class TextareaTagWidget(forms.Textarea):
"""
Textarea widget for tag input.
Useful for cases where many tags are expected or
longer tag names are common.
"""
class TagWidgetMixin:
"""
Mixin providing tag formatting functionality.
Can be used with any form widget to add tag formatting.
"""
def format_value(self, value):
"""
Format tag objects for display in forms.
Parameters:
- value: Tag objects or string value
Returns:
str: Formatted tag string for form display
"""Form classes specifically designed for admin interface functionality.
class MergeTagsForm(forms.Form):
"""
Form for merging multiple tags into one.
Used in the admin interface to combine duplicate
or related tags into a single tag.
"""
new_tag_name = forms.CharField(
label="New Tag Name",
max_length=100,
help_text="Enter the name for the merged tag"
)
def clean_new_tag_name(self):
"""
Validate the new tag name.
Returns:
str: Cleaned tag name
"""from taggit.forms import TagField, TagWidget, TextareaTagWidget
class ArticleForm(forms.Form):
title = forms.CharField(max_length=200)
# Default text input widget
tags = TagField()
# Custom textarea widget for longer tag lists
keywords = TagField(widget=TextareaTagWidget(attrs={'rows': 3}))
# Custom styling
categories = TagField(
widget=TagWidget(attrs={
'class': 'form-control',
'placeholder': 'Enter tags separated by commas'
})
)Integration with Django ModelForms for seamless tag handling in model forms.
from django import forms
from taggit.forms import TagField
from myapp.models import Article
class ArticleModelForm(forms.ModelForm):
class Meta:
model = Article
fields = ['title', 'content', 'tags']
# TaggableManager fields are automatically handled
# but can be customized if needed
tags = TagField(
required=False,
help_text="Enter tags separated by commas"
)Advanced validation patterns for tag input fields.
from django import forms
from django.core.exceptions import ValidationError
from taggit.forms import TagField
class CustomTagField(TagField):
def clean(self, value):
tags = super().clean(value)
# Custom validation: limit number of tags
if len(tags) > 10:
raise ValidationError("Maximum 10 tags allowed")
# Custom validation: tag length
for tag in tags:
if len(tag) > 50:
raise ValidationError(f"Tag '{tag}' is too long (max 50 characters)")
# Custom validation: forbidden words
forbidden = ['spam', 'inappropriate']
for tag in tags:
if tag.lower() in forbidden:
raise ValidationError(f"Tag '{tag}' is not allowed")
return tags
class ArticleForm(forms.Form):
title = forms.CharField(max_length=200)
tags = CustomTagField()Complete examples of processing forms with tag fields.
from django.shortcuts import render, redirect
from django.contrib import messages
from myapp.models import Article
from myapp.forms import ArticleForm
def create_article(request):
if request.method == 'POST':
form = ArticleForm(request.POST)
if form.is_valid():
# Create article
article = Article.objects.create(
title=form.cleaned_data['title'],
content=form.cleaned_data['content']
)
# Add tags
article.tags.set(form.cleaned_data['tags'])
messages.success(request, 'Article created successfully!')
return redirect('article_detail', pk=article.pk)
else:
form = ArticleForm()
return render(request, 'articles/create.html', {'form': form})
def edit_article(request, pk):
article = Article.objects.get(pk=pk)
if request.method == 'POST':
form = ArticleForm(request.POST)
if form.is_valid():
article.title = form.cleaned_data['title']
article.content = form.cleaned_data['content']
article.save()
# Update tags
article.tags.set(form.cleaned_data['tags'])
return redirect('article_detail', pk=article.pk)
else:
# Pre-populate form with existing tags
form = ArticleForm(initial={
'title': article.title,
'content': article.content,
'tags': ', '.join(article.tags.names())
})
return render(request, 'articles/edit.html', {'form': form, 'article': article})Implementing dynamic tag functionality with JavaScript and AJAX.
from django.http import JsonResponse
from django.views.decorators.http import require_GET
from taggit.models import Tag
@require_GET
def tag_autocomplete(request):
"""
Provide tag suggestions for autocomplete functionality.
"""
term = request.GET.get('term', '')
if len(term) < 2:
return JsonResponse({'suggestions': []})
tags = Tag.objects.filter(
name__icontains=term
).values_list('name', flat=True)[:10]
return JsonResponse({'suggestions': list(tags)})
# In template (example JavaScript for autocomplete)
"""
<script>
$('#id_tags').autocomplete({
source: "{% url 'tag_autocomplete' %}",
minLength: 2
});
</script>
"""Understanding how django-taggit parses different tag input formats.
from taggit.utils import parse_tags
# Different input formats and their parsed results
examples = [
('python, django, tutorial', ['django', 'python', 'tutorial']),
('python django tutorial', ['django', 'python', 'tutorial']),
('"web development", python, django', ['django', 'python', 'web development']),
('python,django,tutorial', ['django', 'python', 'tutorial']),
('"multi word tag", single', ['multi word tag', 'single']),
]
for input_str, expected in examples:
result = parse_tags(input_str)
print(f"Input: {input_str}")
print(f"Parsed: {result}")
print(f"Expected: {expected}")
print("---")Template examples for rendering tag forms with proper styling.
<!-- Basic form rendering -->
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Save</button>
</form>
<!-- Custom form rendering -->
<form method="post">
{% csrf_token %}
<div class="form-group">
<label for="{{ form.title.id_for_label }}">Title:</label>
{{ form.title }}
{% if form.title.errors %}
<div class="error">{{ form.title.errors }}</div>
{% endif %}
</div>
<div class="form-group">
<label for="{{ form.tags.id_for_label }}">Tags:</label>
{{ form.tags }}
<small class="help-text">{{ form.tags.help_text }}</small>
{% if form.tags.errors %}
<div class="error">{{ form.tags.errors }}</div>
{% endif %}
</div>
<button type="submit" class="btn btn-primary">Save Article</button>
</form>
<!-- With Bootstrap styling -->
<div class="mb-3">
<label for="{{ form.tags.id_for_label }}" class="form-label">Tags</label>
<input type="{{ form.tags.widget.input_type }}"
name="{{ form.tags.name }}"
value="{{ form.tags.value|default:'' }}"
class="form-control{% if form.tags.errors %} is-invalid{% endif %}"
id="{{ form.tags.id_for_label }}"
placeholder="Enter tags separated by commas">
{% if form.tags.help_text %}
<div class="form-text">{{ form.tags.help_text }}</div>
{% endif %}
{% if form.tags.errors %}
<div class="invalid-feedback">
{% for error in form.tags.errors %}{{ error }}{% endfor %}
</div>
{% endif %}
</div>Using tag fields in Django formsets and inline forms.
from django.forms import modelformset_factory
from myapp.models import Article
# Create formset with tag support
ArticleFormSet = modelformset_factory(
Article,
fields=['title', 'content', 'tags'],
extra=2
)
def bulk_edit_articles(request):
queryset = Article.objects.filter(author=request.user)
if request.method == 'POST':
formset = ArticleFormSet(request.POST, queryset=queryset)
if formset.is_valid():
formset.save()
return redirect('article_list')
else:
formset = ArticleFormSet(queryset=queryset)
return render(request, 'articles/bulk_edit.html', {'formset': formset})Install with Tessl CLI
npx tessl i tessl/pypi-django-taggit