Advanced Usage
This guide covers advanced usage patterns and configurations for Django Flexi Tag’s service-only architecture.
Service-Only Architecture Benefits
Django Flexi Tag uses a service-only architecture that provides several advantages:
Clean QuerySet Composition
from flexi_tag.utils.service import TaggableService
service = TaggableService()
# Start with complex QuerySet
complex_query = (Product.objects
.select_related('category', 'brand')
.prefetch_related('reviews')
.filter(is_active=True)
.filter(created_date__gte=last_month)
.annotate(avg_rating=Avg('reviews__rating')))
# Add tag filtering - all previous filters preserved!
tagged_products = service.filter_by_tag(complex_query, 'featured')
# Chain multiple tag operations
sale_featured = service.filter_by_tag(tagged_products, 'sale')
not_archived = service.exclude_by_tag(sale_featured, 'archived')
Advanced Tag Operations
Multiple Tag Filtering
service = TaggableService()
# AND logic - must have ALL tags
priority_items = service.filter_by_tags(
Product.objects.all(),
['featured', 'sale', 'limited_edition']
)
# OR logic - must have ANY of these tags
special_items = service.filter_by_any_tag(
Product.objects.all(),
['featured', 'sale', 'new_arrival']
)
Performance Optimization
# Use with_tags() to prefetch tag data and avoid N+1 queries
products = service.with_tags(Product.objects.filter(is_active=True))
for product in products:
# No additional database hits here!
tags = product.producttag.tags if hasattr(product, 'producttag') else []
Conditional Tag Operations
def apply_business_rules(order):
service = TaggableService()
# Auto-tag based on business logic
if order.total_amount > 10000:
service.add_tag(order, 'high_value')
if order.customer.is_vip:
service.add_tag(order, 'vip_customer')
if order.created_date == timezone.now().date():
service.add_tag(order, 'today')
# Remove expired tags
existing_tags = service.get_tags(order)
if 'flash_sale' in existing_tags:
if not order.is_flash_sale_active():
service.remove_tag(order, 'flash_sale')
Bulk Operations and Performance
Efficient Bulk Processing
def process_monthly_orders():
"""Process all orders from last month with batch operations"""
last_month = timezone.now() - timedelta(days=30)
orders = Order.objects.filter(created_date__gte=last_month)
service = TaggableService()
# Batch tag all orders from last month
service.bulk_add_tags_with_many_instances(orders, ['processed', 'archived'])
# Remove temporary tags efficiently
temp_tagged = service.filter_by_tag(orders, 'temporary')
service.bulk_remove_tags_with_many_instances(temp_tagged, ['temporary'])
Custom Exception Integration
Django Flexi Tag supports configurable base exception classes for seamless integration with your project’s exception hierarchy.
Basic Configuration
# settings.py
FLEXI_TAG_BASE_EXCEPTION_CLASS = 'myproject.exceptions.BaseAPIException'
Your Custom Base Exception
# myproject/exceptions.py
class BaseAPIException(Exception):
"""Base exception for all API errors"""
def __init__(self, message, status_code=400, error_code=None, *args, **kwargs):
super().__init__(message, *args, **kwargs)
self.status_code = status_code
self.error_code = error_code
Enhanced Exception Handling
from flexi_tag.exceptions import TagValidationException
try:
service.add_tag(product, "duplicate_tag")
except TagValidationException as e:
print(e) # "tag_100_1:Tag already exists. name: duplicate_tag"
print(e.status_code) # 400 (inherited from BaseAPIException)
print(e.error_code) # None (inherited from BaseAPIException)
Django REST Framework Integration
# settings.py
FLEXI_TAG_BASE_EXCEPTION_CLASS = 'rest_framework.exceptions.APIException'
# Now all flexi-tag exceptions work seamlessly with DRF
from flexi_tag.exceptions import TagValidationException
from rest_framework.exceptions import APIException
def my_view(request):
try:
service.add_tag(instance, "invalid_tag")
except APIException as e: # Can catch as DRF exception!
return Response(
{"error": str(e)},
status=e.status_code if hasattr(e, 'status_code') else 400
)
Available Exception Types
All these exceptions support custom base class configuration:
TagValidationException- Tag already exists or validation failsTagNotFoundException- Tag not found during removalTagNotDefinedException- Required tag parameter missingObjectIDsNotDefinedException- Required object IDs missing
Error Codes
Each exception has a unique error code:
from flexi_tag.exceptions import TagValidationException
from flexi_tag import codes
exception = TagValidationException(name="test")
print(exception.code) # Same as codes.tag_100_1
Custom Tag Model Configuration
The generate_tag_models command creates tag models with default settings, but you might want to customize this generation. You can create your own management command that extends the default one:
from django.core.management.base import BaseCommand
from flexi_tag.management.commands.generate_tag_models import Command as BaseGenerateTagModelsCommand
class Command(BaseGenerateTagModelsCommand):
help = "Generate custom tag models for all models that inherit from FlexiTagMixin"
def handle(self, *args, **options):
# Customize the model template
self.model_template = """
# Custom model template
from django.db import models
from flexi_tag.utils.compat import JSONField
class {{ model_name }}Tag(models.Model):
instance = models.OneToOneField(
"{{ app_label }}.{{ model_name }}",
on_delete=models.CASCADE,
primary_key=True,
)
tags = JSONField(default=list)
# Add custom fields here
last_tagged_at = models.DateTimeField(auto_now=True)
class Meta:
app_label = "{{ app_label }}"
db_table = "{{ app_label }}_{{ model_lower_name }}_tag"
"""
super().handle(*args, **options)
Tag Validation
You can implement custom tag validation by extending the TaggableService class:
from flexi_tag.utils.service import TaggableService
from flexi_tag.exceptions import TagValidationException
class MyTaggableService(TaggableService):
def __validate_tag_key(self, key: str) -> bool:
# Call the parent implementation
super().__validate_tag_key(key)
# Add custom validation
if len(key) < 3:
raise TagValidationException(name=key, message="Tag must be at least 3 characters long")
# Only allow alphanumeric tags
if not key.isalnum():
raise TagValidationException(name=key, message="Tag must be alphanumeric")
return True
Querying Tagged Objects
To efficiently query objects by their tags, you can use PostgreSQL’s JSON operators:
# Find all objects with a specific tag
objects_with_tag = YourModel.objects.filter(yourmodeltag__tags__contains=["important"])
# Find objects with any of these tags
objects_with_any_tag = YourModel.objects.filter(yourmodeltag__tags__overlap=["urgent", "important"])
Using with Non-PostgreSQL Databases
While Django Flexi Tag is optimized for PostgreSQL using its native JSON support, you can use it with other databases by customizing the tag model generation. For example, to use it with SQLite or MySQL:
Create a custom JSONField implementation
Update the model template in a custom management command
Ensure your database can efficiently query the tag field
Troubleshooting
Model Detection Issues
If the generate_tag_models command doesn’t detect your newly added FlexiTagMixin models:
# Try force reloading models
python manage.py generate_tag_models --force-reload
# Or restart your Django development server and try again
python manage.py runserver
Common causes:
Models module hasn’t been imported yet
Django’s model cache hasn’t been updated
Circular import issues
Template Engine Issues
If you encounter template engine configuration errors:
# Ensure your settings.py has proper TEMPLATES configuration
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
# ... your context processors
],
},
},
]
Performance Considerations
For large datasets, consider these performance optimizations:
Create database indexes on the tags field
Use batch processing for bulk tag operations
Consider denormalizing critical tag data for faster queries
Use caching for frequently accessed tag information
Security Considerations
When implementing tag systems, be aware of these security concerns:
Validate tag input to prevent injection attacks
Implement permission checks for tag management
Consider the visibility of tags in your API responses
Audit tag changes for sensitive data