CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-django-taggit

A reusable Django application for simple tagging with comprehensive manager and form support.

Pending
Overview
Eval results
Files

rest-framework.mddocs/

REST Framework Integration

Django REST framework serializers and fields for handling tags in API endpoints. Django-taggit provides comprehensive integration with Django REST Framework, enabling seamless serialization and deserialization of tags in API responses and requests.

Capabilities

TagListSerializerField

A specialized serializer field for handling tag lists in REST API endpoints.

class TagListSerializerField(serializers.ListField):
    """
    Serializer field for handling tag lists.
    
    Supports both string and list input formats, with optional
    pretty printing and custom ordering.
    """
    child = serializers.CharField()
    
    def __init__(self, **kwargs):
        """
        Initialize the field.
        
        Parameters:
        - pretty_print (bool): Enable pretty JSON formatting
        - style (dict): Widget style options
        - **kwargs: Additional field options
        """
    
    def to_internal_value(self, value):
        """
        Convert input to internal representation.
        
        Parameters:
        - value: String (JSON) or list of tag names
        
        Returns:
        list: List of tag name strings
        
        Raises:
        ValidationError: If input format is invalid
        """
    
    def to_representation(self, value):
        """
        Convert internal value to JSON representation.
        
        Parameters:
        - value: Tag manager or list of tags
        
        Returns:
        TagList: Formatted list of tag names
        """

TaggitSerializer

A mixin serializer for models that use TaggableManager fields.

class TaggitSerializer(serializers.Serializer):
    """
    Mixin serializer for handling tagged models.
    
    Automatically handles TagListSerializerField instances
    during create and update operations.
    """
    
    def create(self, validated_data):
        """
        Create instance with tag handling.
        
        Parameters:
        - validated_data (dict): Validated serializer data
        
        Returns:
        Model instance with tags applied
        """
    
    def update(self, instance, validated_data):
        """
        Update instance with tag handling.
        
        Parameters:
        - instance: Model instance to update
        - validated_data (dict): Validated serializer data
        
        Returns:
        Updated model instance with tags applied
        """

TagList

A specialized list class for tag representation with pretty printing support.

class TagList(list):
    """
    List subclass with pretty printing support.
    
    Enhances regular Python lists with JSON formatting
    capabilities for API responses.
    """
    
    def __init__(self, *args, pretty_print=True, **kwargs):
        """
        Initialize TagList.
        
        Parameters:
        - pretty_print (bool): Enable formatted JSON output
        """
    
    def __str__(self):
        """
        String representation with optional pretty printing.
        
        Returns:
        str: JSON-formatted string representation
        """

Basic Serializer Integration

Simple Model Serializer

Basic integration with a model that has TaggableManager fields.

from rest_framework import serializers
from taggit.serializers import TaggitSerializer, TagListSerializerField
from myapp.models import Article

class ArticleSerializer(TaggitSerializer, serializers.ModelSerializer):
    tags = TagListSerializerField()
    
    class Meta:
        model = Article
        fields = ['id', 'title', 'content', 'tags', 'created_at']

Multiple Tag Fields

Handling models with multiple TaggableManager fields.

class BlogPostSerializer(TaggitSerializer, serializers.ModelSerializer):
    tags = TagListSerializerField()
    categories = TagListSerializerField()
    
    class Meta:
        model = BlogPost
        fields = ['id', 'title', 'content', 'tags', 'categories', 'published_at']

# Usage in views
class BlogPostViewSet(viewsets.ModelViewSet):
    queryset = BlogPost.objects.all()
    serializer_class = BlogPostSerializer
    
    def get_queryset(self):
        # Optimize queries with prefetch_related
        return super().get_queryset().prefetch_related('tags', 'categories')

Custom Field Configuration

Customizing TagListSerializerField behavior.

class CustomArticleSerializer(TaggitSerializer, serializers.ModelSerializer):
    tags = TagListSerializerField(
        required=False,
        allow_empty=True,
        help_text="List of tags for this article"
    )
    
    # Custom ordering for tags
    ordered_tags = TagListSerializerField(
        source='tags',
        order_by=['name']
    )
    
    class Meta:
        model = Article
        fields = ['id', 'title', 'content', 'tags', 'created_at']

API Input/Output Formats

JSON Input Formats

Different ways to send tag data to the API.

# Example API requests
import requests

# List format (preferred)
data = {
    'title': 'My Article',
    'content': 'Article content...',
    'tags': ['python', 'django', 'tutorial']
}

# String format (also supported)
data = {
    'title': 'My Article',
    'content': 'Article content...',
    'tags': '["python", "django", "tutorial"]'
}

response = requests.post('/api/articles/', json=data)

JSON Output Format

How tags appear in API responses.

{
    "id": 1,
    "title": "My Article",
    "content": "Article content...",
    "tags": ["python", "django", "tutorial"],
    "created_at": "2024-01-15T10:30:00Z"
}

Advanced Serializer Patterns

Nested Tag Information

Including additional tag information in API responses.

class TagSerializer(serializers.ModelSerializer):
    class Meta:
        model = Tag
        fields = ['name', 'slug']

class DetailedArticleSerializer(TaggitSerializer, serializers.ModelSerializer):
    tags = TagSerializer(many=True, read_only=True)
    tag_names = TagListSerializerField(write_only=True)
    
    class Meta:
        model = Article
        fields = ['id', 'title', 'content', 'tags', 'tag_names', 'created_at']
    
    def create(self, validated_data):
        tag_names = validated_data.pop('tag_names', [])
        instance = super().create(validated_data)
        instance.tags.set(tag_names)
        return instance
    
    def update(self, instance, validated_data):
        tag_names = validated_data.pop('tag_names', None)
        instance = super().update(instance, validated_data)
        if tag_names is not None:
            instance.tags.set(tag_names)
        return instance

Tag Filtering and Search

Implementing tag-based filtering in API views.

from django_filters import rest_framework as filters
from rest_framework import viewsets

class ArticleFilter(filters.FilterSet):
    tags = filters.CharFilter(method='filter_by_tags')
    has_tag = filters.CharFilter(field_name='tags__name', lookup_expr='iexact')
    
    def filter_by_tags(self, queryset, name, value):
        """Filter by multiple tags (comma-separated)."""
        tag_names = [tag.strip() for tag in value.split(',')]
        return queryset.filter(tags__name__in=tag_names).distinct()
    
    class Meta:
        model = Article
        fields = ['tags', 'has_tag']

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    filterset_class = ArticleFilter
    search_fields = ['title', 'content', 'tags__name']
    
    def get_queryset(self):
        return super().get_queryset().prefetch_related('tags')

Tag Statistics in APIs

Providing tag usage statistics through API endpoints.

from django.db.models import Count
from rest_framework.decorators import action
from rest_framework.response import Response

class TagViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Tag.objects.all()
    serializer_class = TagSerializer
    
    @action(detail=False, methods=['get'])
    def popular(self, request):
        """Get most popular tags."""
        popular_tags = Tag.objects.annotate(
            usage_count=Count('tagged_items')
        ).filter(usage_count__gt=0).order_by('-usage_count')[:10]
        
        data = [
            {
                'name': tag.name,
                'slug': tag.slug,
                'usage_count': tag.usage_count
            }
            for tag in popular_tags
        ]
        return Response(data)
    
    @action(detail=True, methods=['get'])
    def articles(self, request, pk=None):
        """Get articles for a specific tag."""
        tag = self.get_object()
        articles = Article.objects.filter(tags=tag)
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data)

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    
    @action(detail=False, methods=['get'])
    def by_tag(self, request):
        """Get articles grouped by tags."""
        tag_name = request.query_params.get('tag')
        if not tag_name:
            return Response({'error': 'tag parameter required'}, status=400)
        
        articles = Article.objects.filter(tags__name__iexact=tag_name)
        serializer = self.get_serializer(articles, many=True)
        return Response({
            'tag': tag_name,
            'count': articles.count(),
            'articles': serializer.data
        })

Validation and Error Handling

Custom validation for tag input in serializers.

from rest_framework import serializers
from django.core.exceptions import ValidationError

class ValidatedArticleSerializer(TaggitSerializer, serializers.ModelSerializer):
    tags = TagListSerializerField()
    
    class Meta:
        model = Article
        fields = ['id', 'title', 'content', 'tags']
    
    def validate_tags(self, value):
        """Custom tag validation."""
        if len(value) > 10:
            raise serializers.ValidationError("Maximum 10 tags allowed")
        
        for tag in value:
            if len(tag) > 50:
                raise serializers.ValidationError(f"Tag '{tag}' is too long (max 50 characters)")
            
            if not tag.replace(' ', '').replace('-', '').isalnum():
                raise serializers.ValidationError(f"Tag '{tag}' contains invalid characters")
        
        return value
    
    def validate(self, data):
        """Cross-field validation."""
        tags = data.get('tags', [])
        title = data.get('title', '')
        
        # Ensure title doesn't conflict with tags
        if title.lower() in [tag.lower() for tag in tags]:
            raise serializers.ValidationError("Title cannot be the same as a tag")
        
        return data

Permissions and Access Control

Implementing tag-based permissions in API views.

from rest_framework.permissions import BasePermission

class TagPermission(BasePermission):
    """Custom permission for tag operations."""
    
    def has_permission(self, request, view):
        if request.method in ['GET', 'HEAD', 'OPTIONS']:
            return True
        return request.user.is_authenticated
    
    def has_object_permission(self, request, view, obj):
        if request.method in ['GET', 'HEAD', 'OPTIONS']:
            return True
        
        # Only allow editing of own articles
        return obj.author == request.user

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    permission_classes = [TagPermission]
    
    def perform_create(self, serializer):
        serializer.save(author=self.request.user)

Bulk Operations

Handling bulk tag operations through the API.

from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import status

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    
    @action(detail=False, methods=['post'])
    def bulk_tag(self, request):
        """Add tags to multiple articles."""
        article_ids = request.data.get('article_ids', [])
        tags = request.data.get('tags', [])
        
        if not article_ids or not tags:
            return Response(
                {'error': 'article_ids and tags are required'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        articles = Article.objects.filter(id__in=article_ids)
        updated_count = 0
        
        for article in articles:
            article.tags.add(*tags)
            updated_count += 1
        
        return Response({
            'updated_count': updated_count,
            'tags_added': tags
        })
    
    @action(detail=False, methods=['post'])
    def bulk_untag(self, request):
        """Remove tags from multiple articles."""
        article_ids = request.data.get('article_ids', [])
        tags = request.data.get('tags', [])
        
        articles = Article.objects.filter(id__in=article_ids)
        updated_count = 0
        
        for article in articles:
            article.tags.remove(*tags)
            updated_count += 1
        
        return Response({
            'updated_count': updated_count,
            'tags_removed': tags
        })

Install with Tessl CLI

npx tessl i tessl/pypi-django-taggit

docs

admin-interface.md

form-integration.md

index.md

model-integration.md

rest-framework.md

tag-operations.md

utilities-management.md

view-helpers.md

tile.json