============= Example Usage ============= This section contains code examples of how to set up and use Tagulous. If you'd like a more interactive demonstration, there is a `static demo `_ showing the front-end, or an `example project `_ for you to install locally and play with some of these code examples. .. _example_auto_tagmodel: Automatic tag models ==================== This simple example creates a ``SingleTagField`` (a glorified ``ForeignKey``) and two ``TagField`` (a typical tag field, using ``ManyToManyField``):: from django.db import models import tagulous.models class Person(models.Model): title = tagulous.models.SingleTagField( label="Your preferred title", initial="Mr, Mrs, Ms", ) name = models.CharField(max_length=255) skills = tagulous.models.TagField( force_lowercase=True, max_count=5, ) * This will create two new models at runtime to store the tags, ``Tagulous_Person_title`` and ``Tagulous_Person_skills``. * These models will act like normal models, and can be managed in the database using Django migrations * ``Person.title`` will now act as a ``ForeignKey`` to ``Tagulous_Person_title`` * ``Person.skills`` will now act as a ``ManyToManyField`` to ``Tagulous_Person_skills`` Initial tags need to be loaded into the database with the :ref:`command_initial_tags` management command. You can use the fields to assign and query values:: # Person.skills.tag_model == Tagulous_Person_skills # Set tags on an instance with a string instance = Person() instance.skills = 'run, "kung fu", jump' # They're not committed to the database until you save instance.save() # Get a list of all tags tags = Person.skills.tag_model.objects.all() # Assign a list of tags instance.skills = ['jump', 'kung fu'] # Tags are readable before saving # str(instance.skills) == 'jump, "kung fu"' instance.save() # Step through the list of instances in the tag model for skill in instance.skills.all(): do_something(skill) # Compare tag fields if instance.skills == other.skills: return True .. _example_custom_tag_model: Custom models ============= You can create a tag model manually, and specify it in one or more tag fields:: import tagulous.models class Hobbies(tagulous.models.TagModel): class TagMeta: # Tag options initial = "eating, coding, gaming" force_lowercase = True autocomplete_view = 'myapp.views.hobbies_autocomplete' class Person(models.Model): name = models.CharField(max_length=255) hobbies = tagulous.models.TagField(to=Hobbies) Options for a custom tag model must be set in :ref:`tagmeta` - you cannot pass them as arguments in tag fields. See :doc:`models/tag_models` to see which field names Tagulous uses internally. .. _example_tag_trees: Tag Trees ========= A tag field can specify ``tree=True`` to use slashes in tag names to denote children:: import tagulous.models class Person(models.Model): name = models.CharField(max_length=255) skills = tagulous.models.TagField( force_lowercase=True, max_count=5, tree=True, ) This can't be set in the tag model's ``TagMeta`` object; the tag model must instead subclass :ref:`tagtreemodel`:: class Hobbies(tagulous.models.TagTreeModel): class TagMeta: initial = "food/eating, food/cooking, gaming/football" force_lowercase = True autocomplete_view = 'myapp.views.hobbies_autocomplete' class Person(models.Model): name = models.CharField(max_length=255) hobbies = tagulous.models.TagField(to=Hobbies) You can add tags as normal, and then query using tree relationships:: person.hobbies = "food/eating/mexican, sport/football" person.save() # Get all root nodes: "food", "gaming" and "sport" root_nodes = Hobbies.objects.filter(parent=None) # Get the direct children of food: "food/eating", "food/cooking" food_children = Hobbies.objects.get(name="food").children.all() # Get all descendants of food: # "food/eating", "food/eating/mexican", "food/cooking" food_children = Hobbies.objects.get(name="food").get_descendants() See :doc:`models/tag_trees` to see a full list of available tree methods and properties. .. _example_tag_url: Tag URL ======= You can set the ``get_absolute_url`` tag option to a callable to give tag objects absolute URLs without needing to create a custom tag model:: from django.db import models from django.core.urlresolvers import reverse import tagulous.models class Person(models.Model): name = models.CharField(max_length=255) skills = tagulous.models.TagField( get_absolute_url=lambda tag: reverse( 'myapp.views.by_skill', kwargs={'skill_slug': tag.slug} ), ) The ``get_absolute_url`` method can now be called as normal; for example, from a template:: {% for skill in person.skills.all %} {{ skill.name }} {% endfor %} If you are using a tree, you will want to use the path instead:: skills = tagulous.models.TagField( tree=True, get_absolute_url=lambda tag: reverse( 'myapp.views.by_skill', kwargs={'skill_path': tag.path} ), ) See the :ref:`option_get_absolute_url` option for more details. .. _example_modelform: ModelForms ========== A ``ModelForm`` with tag fields needs no special treatment:: from django.db import models from django import forms import tagulous.models class Person(models.Model): name = models.CharField(max_length=255) skills = tagulous.models.TagField() class PersonForm(forms.ModelForm): class Meta: fields = ['name', 'skills'] model = Person They are normal forms so can be used in normal ways; for example, with class-based views:: from django.views.generic.edit import CreateView class PersonCreate(CreateView): model = Person fields = ['name', 'skills'] or with view functions:: def person_create(request, template_name="my_app/person_form.html"): form = PersonForm(request.POST or None) if form.is_valid(): form.save() return redirect('home') return render(request, template_name, {'form': form}) However, because a ``TagField`` is based on a ``ManyToManyField``, if you save your form using ``commit=False``, you will need to call ``save_m2m`` to save the tags:: class Pet(models.Model): owner = models.ForeignKey('auth.User') name = models.CharField(max_length=255) skills = tagulous.models.TagField() class PetForm(forms.ModelForm): class Meta: fields = ['owner', 'name', 'skills'] model = Pet def pet_create(request, template_name="my_app/pet_form.html"): form = PetForm(request.POST or None) if form.is_valid(): pet = form.save(commit=False) pet.owner = request.user # Next line will save all non M2M fields (including SingleTagField) pet.save() # Next line will save any ``TagField`` values form.save_m2m() return redirect('home') return render(request, template_name, {'form': form}) As shown above, this only applies to ``TagField`` - a ``SingleTagField`` is based on ``ForeignKey``, so will be saved without needing ``save_m2m``. See :doc:`forms` for how to use tag fields in forms. .. _example_form: Forms without models ==================== Tagulous form fields take tag options as a single ``TagOptions`` object, rather than as separate arguments as a model form does:: from django import forms import tagulous.forms class PersonForm(forms.ModelForm): title = tagulous.forms.SingleTagField( autocomplete_tags=['Mr', 'Mrs', 'Ms'] ) name = forms.CharField(max_length=255) skills = tagulous.forms.TagField( tag_options=tagulous.models.TagOptions( force_lowercase=True, ), autocomplete_tags=['running', 'jumping', 'judo'] ) A ``SingleTagField`` will return a string, and a ``TagField`` will return a list of strings:: form = PersonForm(data={ 'title': 'Mx', 'skills': 'Running, judo', }) assert form.is_valid() assert form.cleaned_data['title'] == 'Mx' assert form.cleaned_data['skills'] == ['running', 'judo'] See :doc:`forms` for how to use tag fields in forms. .. _example_filter_embedded: Filtering embedded autocomplete =============================== Filtering autocomplete to initial tags only ------------------------------------------- If it often useful for autocomplete to only list your initial tags, and not those added by others; Tagulous makes this easy with the ``autocomplete_initial`` field option:: class Person(models.Model): title = tagulous.models.SingleTagField( label="Your preferred title", initial="Mr, Mrs, Ms", autocomplete_initial=True, ) Even if users add new tags, only the initial tags will ever be shown as autocomplete options. See :ref:`option_autocomplete_initial` for more details. .. _example_filter_related: Filtering autocomplete by related fields ---------------------------------------- This example will embed the tags into the HTML of the response; if you are using autocomplete views, see :ref:`example_filter_autocomplete_view` instead. Filter the ``autocomplete_tags`` queryset after the form initialises:: from django.db import models from django import forms import tagulous class Pet(models.Model): owner = models.ForeignKey('auth.User') name = models.CharField(max_length=255) skills = tagulous.models.TagField() class PetForm(forms.ModelForm): def __init__(self, user, *args, **kwargs): super(PetForm, self).__init__(*args, **kwargs) # Filter skills to initial skills, or ones added by this user self.fields['skills'].autocomplete_tags = \ self.fields['skills'].autocomplete_tags.filter_or_initial( pet__owner=user ).distinct() class Meta: model = Pet Then call ``PetForm`` with the user as the first argument, for example:: def add_pet(request): form = PetForm(request.user) # ... For more details, see :ref:`filter_by_related` and :ref:`filter_autocomplete`. .. _example_autocomplete_views: Autocomplete AJAX Views ======================= To use AJAX to populate your autocomplete using JavaScript, set the tag option ``autocomplete_view`` in your models to a value for ``reverse()``:: class Person(models.Model): name = models.CharField(max_length=255) skills = tagulous.models.TagField( autocomplete_view='person_skills_autocomplete' ) You can then use the default autocomplete views directly in your urls:: import tagulous from myapp.models import Person urlpatterns = [ url( r'^person/skills/autocomplete/', tagulous.views.autocomplete, {'tag_model': Person}, name='person_skills_autocomplete', ), ] See :doc:`views` for more details. .. _example_filter_autocomplete_view: Filtering an autocomplete view ------------------------------ Add a wrapper function which filters the queryset before it calls the normal ``autocomplete`` view:: @login_required def autocomplete_pet_skills(request): return tagulous.views.autocomplete( request, Pet.skills.tag_model.objects.filter_or_initial( pet__owner=user ).distinct() ) Django REST Framework ===================== The Django REST framework's ``ModelSerializer`` will serialize tag fields to their primary keys; for example:: class PersonKeySerializer(ModelSerializer): class Meta: model = Person fields = ["name", "title", "skills"] person = Person.objects.create(name="adam", title="mr", skills="run, jump") PersonKeySerializer(Person).data == { "name": "adam", "title": 1, "skills": [1, 2] If you'd prefer to serialize to strings, use the Tagulous ``TagSerializer``:: from tagulous.contrib.drf import TagSerializer class PersonStringSerializer(TagSerializer): class Meta: model = Person fields = ["name", "title", "skills"] person = Person.objects.create(name="adam", title="mr", skills="run, jump") PersonStringSerializer(Person).data == { "name": "adam", "title": "mr", "skills": ["run", "jump"]