============= 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: .. code-block:: python 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: .. code-block:: bash # 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: .. code-block:: python # 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: .. code-block:: python # 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: .. code-block:: python # 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: .. code-block:: bash python manage.py migrate Adding API Support =============== To expose tagging functionality through a REST API, add the TaggableViewSetMixin to your ViewSets: .. code-block:: python 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 ----------------- .. code-block:: http POST /api/products/1/add_tag/ Content-Type: application/json { "key": "featured" } Adding Multiple Tags ----------------- .. code-block:: http POST /api/products/1/bulk_add_tag/ Content-Type: application/json { "keys": ["new", "sale", "limited-edition"] } Removing a Tag ------------ .. code-block:: http POST /api/products/1/remove_tag/ Content-Type: application/json { "key": "featured" } Removing Multiple Tags ------------------- .. code-block:: http POST /api/products/1/bulk_remove_tags/ Content-Type: application/json { "keys": ["new", "sale"] } Bulk Operations ------------- Add tags to multiple products: .. code-block:: http POST /api/products/bulk_add_tags/ Content-Type: application/json { "objects": [1, 2, 3, 4], "keys": ["clearance", "last-chance"] } Remove tags from multiple products: .. code-block:: http 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: .. code-block:: python 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: .. code-block:: python 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 ============= .. code-block:: python # 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: .. code-block:: python # 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: .. code-block:: python 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: .. code-block:: python 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: * :doc:`advanced` - For custom tag validation, advanced queries, and more * :doc:`api` - For a complete API reference