from django.db import models
from taggit.managers import TaggableManager
class Post(models.Model):
#...
tags = TaggableManager()
Теперь при создании объекта класса Post, можно указывать нужные теги, а затем получать их:
post = Post.objects.create(name="sample")
post.tags.add("tag1", "tag2", "tag3")
tags = post.tags.all()
Всё хорошо, но существуют ситуации, когда на странице необходимо вывести список, состоящий из большого количества записей (в данном случае, постов) и вывести список тегов для каждой записи. При стандартной реализации в шаблоне Django это выглядит примерно так:
{% for post in posts %}
<div class="post ">
<h2 class="post_name">{{ post.name }}</h2>
<div class="post_text">
{{ post.text }}
</div>
<div class="post_tags">
Теги:
{% for tag in post.tags.all %}
<b>{{ tag.name }} </b>
{% endfor %}
</div>
</div>
{% endfor %}
Такое решение содержит в себе минус: при обходе постов на каждой итерации при вызове "post.tags.all" производится запрос к базе данных, в результате чего количество запросов растет с увеличением количества записей на странице.Очевидно, что гораздо лучше выбирать все теги сразу для всех постов, а не для каждого отдельно. Для этой цели я решил использовать метод prefetch_related. Данный метод применим для отношений "многие–ко–многим" и позволяет при выборе списка записей сразу выбрать из базы данных нужные связанные записи. В конечном итоге, для удобства был создан управляющий класс модели (Manager), который переопределяет метод get_query_set таким образом, чтобы при его вызове использовался метод prefetch_related и выбирались связанные записи тегов. Данный управляющий класс можно прикрепить к любой модели, имеющей теги и уже при его помощи выбирать объекты данной модели:
#управляющий класс
class TaggedManager(models.Manager):
def get_query_set(self):
return super(TaggedManager, self).get_query_set().prefetch_related('tagged_items__tag')
#...
#немного изменим класс Post
class Post(models.Model):
#...
tagged = TaggedManager()
objects = models.Manager()
#...
#теперь получаем посты так
posts = Post.tagged.all()
И немного поменяем шаблон, чтобы при выводе тегов обращение происходило непосредственно к уже выбранным связям:
...
<div class="post_tags">
Теги:
{% for item in post.tagged_items.all %}
{% with item.tag as tag %}
<b>{{ tag.name }} </b>
{% endwith %}
{% endfor %}
</div>
В общем-то, все, оптимизация завершена. Может быть, это не самое красивое решение, но оно работает: при выборе постов выбираются сразу все связанные с ними теги, и затем, при их выводе обращение происходит к кешу, а не к базе данных.