Quick Start
This guide will help you quickly implement tagging functionality in your Django application using dj-flexi-tag’s service-only architecture.
Preparing Your Models
First, let’s make your existing models “taggable” by adding the FlexiTagMixin:
from django.db import models
from flexi_tag.utils.models import FlexiTagMixin
class Product(FlexiTagMixin):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
# other fields...
class Article(FlexiTagMixin):
title = models.CharField(max_length=200)
content = models.TextField()
# other fields...
The FlexiTagMixin doesn’t add any fields to your models - it simply marks them for tag model generation.
Generating Tag Models
After adding the FlexiTagMixin to your models, run the management command to generate the corresponding tag models:
# Standard usage
python manage.py generate_tag_models
# For testing what would be generated without creating files
python manage.py generate_tag_models --dry-run
# If models aren't detected after recent changes (development mode)
python manage.py generate_tag_models --force-reload
This command will create files named flexi_generated_model.py in the same directories as your models. It will also automatically run the makemigrations command to create the necessary migration files for the new tag models.
Note
If you’ve just added FlexiTagMixin to a model and the command doesn’t detect it,
try using the --force-reload flag or restart your Django development server.
For example, the generated tag model will look like this:
# products/flexi_generated_model.py
# This file is auto-generated. Do not edit manually.
# Generated by the generate_tag_models command.
from django.db import models
from flexi_tag.utils.compat import GinIndex, JSONField
class ProductTag(models.Model):
instance = models.OneToOneField(
"products.Product",
on_delete=models.CASCADE,
primary_key=True,
)
tags = JSONField(default=list)
class Meta:
app_label = "products"
db_table = "products_product_tag"
indexes = [GinIndex(fields=["tags"])]
def __str__(self):
return "Tags for {}".format(self.instance)
The command will also automatically add the necessary import to your original models.py file:
# At the bottom of your imports in models.py
from .flexi_generated_model import ProductTag # noqa
If you have multiple models with the FlexiTagMixin in the same app, the imports will be combined in a single line:
# At the bottom of your imports in models.py
from .flexi_generated_model import ArticleTag, ProductTag # noqa
Creating and Applying Migrations
After the tag models have been generated and migrations created automatically, you only need to apply the migrations:
python manage.py migrate
Adding API Support
To expose tagging functionality through a REST API, add the TaggableViewSetMixin to your ViewSets:
from rest_framework import viewsets
from flexi_tag.utils.views import TaggableViewSetMixin
from .models import Product
from .serializers import ProductSerializer
class ProductViewSet(viewsets.ModelViewSet, TaggableViewSetMixin):
queryset = Product.objects.all()
serializer_class = ProductSerializer
This adds the following endpoints to your ViewSet:
POST /products/{id}/add_tag/ - Add a tag to a product
POST /products/{id}/bulk_add_tag/ - Add multiple tags to a product
POST /products/{id}/remove_tag/ - Remove a tag from a product
POST /products/{id}/bulk_remove_tags/ - Remove multiple tags from a product
POST /products/bulk_add_tags/ - Add tags to multiple products
POST /products/bulk_remove_tags_with_many_instances/ - Remove tags from multiple products
Using the API
Adding a Single Tag
POST /api/products/1/add_tag/
Content-Type: application/json
{
"key": "featured"
}
Removing a Tag
POST /api/products/1/remove_tag/
Content-Type: application/json
{
"key": "featured"
}
Bulk Operations
Add tags to multiple products:
POST /api/products/bulk_add_tags/
Content-Type: application/json
{
"objects": [1, 2, 3, 4],
"keys": ["clearance", "last-chance"]
}
Remove tags from multiple products:
POST /api/products/bulk_remove_tags_with_many_instances/
Content-Type: application/json
{
"objects": [1, 2, 3, 4],
"keys": ["new-arrival"]
}
Programmatic Usage (Service-Only Architecture)
The core power of dj-flexi-tag comes from its service-only architecture that works seamlessly with any QuerySet:
from flexi_tag.utils.service import TaggableService
# Create a service instance
service = TaggableService()
# Get a product instance
product = Product.objects.get(id=1)
# Add tags to instances
service.add_tag(instance=product, key="featured")
service.bulk_add_tags(instance=product, keys=["sale", "new"])
# Remove tags
service.remove_tag(instance=product, key="featured")
# Get all tags for an instance
tags = service.get_tags(product)
QuerySet Filtering (The Power of Service-Only!)
This is where the service-only approach truly shines - you can compose with any existing QuerySet:
from flexi_tag.utils.service import TaggableService
service = TaggableService()
# Start with your existing QuerySet - all filters preserved!
products = (Product.objects
.filter(is_active=True)
.select_related('category')
.prefetch_related('reviews'))
# Add tag filtering - preserves all existing filters!
featured_products = service.filter_by_tag(products, 'featured')
sale_products = service.exclude_by_tag(products, 'out_of_stock')
# Multiple tag filtering
priority_products = service.filter_by_tags(products, ['featured', 'sale'])
any_special = service.filter_by_any_tag(products, ['featured', 'sale', 'new'])
# Performance optimization - prefetch tag data
products_with_tags = service.with_tags(products)
Bulk Operations
# Bulk operations on multiple instances
products = Product.objects.filter(in_stock=True)
service.bulk_add_tags_with_many_instances(instances=products, keys=["available"])
service.bulk_remove_tags_with_many_instances(instances=products, keys=["out_of_stock"])
Custom Exception Integration (Optional)
You can configure dj-flexi-tag to use your project’s base exception class:
# settings.py
FLEXI_TAG_BASE_EXCEPTION_CLASS = 'myproject.exceptions.BaseAPIException'
# Or for DRF projects:
FLEXI_TAG_BASE_EXCEPTION_CLASS = 'rest_framework.exceptions.APIException'
Now all flexi-tag exceptions will inherit from your base class:
from flexi_tag.exceptions import TagValidationException
try:
service.add_tag(product, 'duplicate_tag')
except TagValidationException as e:
# Now has your custom base class attributes!
print(e.status_code) # From your BaseAPIException
API Integration Example
If you’re using Django REST Framework, you can integrate tag filtering in your views:
from rest_framework.generics import ListAPIView
from flexi_tag.utils.service import TaggableService
class ProductListAPIView(ListAPIView):
serializer_class = ProductSerializer
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.taggable_service = TaggableService()
def get_queryset(self):
queryset = Product.objects.filter(is_active=True)
# Apply tag filter if provided - preserves existing filters!
tag = self.request.query_params.get('tag')
if tag:
queryset = self.taggable_service.filter_by_tag(queryset, tag)
return queryset
# Usage: /api/products/?tag=featured
Next Steps
Now that you have basic tagging functionality working, you can explore:
Advanced Usage - For custom tag validation, advanced queries, and more
API Reference - For a complete API reference