========= Utilities ========= The following section documents general utilities available with Mezzanine. While these aren't a core part of Mezzanine itself, they're widely used across many areas of Mezzanine, and can be very useful in conjunction with your own custom content and features. Firstly covered are the utilities found in the :mod:`mezzanine.generic` app, such as :ref:`keywords`, :ref:`comments`, and :ref:`ratings`. Each of these form a common pattern: * A model is provided containing generic relationships using Django's `django.contrib.contenttypes `_ app * A custom model field is provided for defining relationships to the :mod:`mezzanine.generic` model, which can then be applied to any of your own models * The custom field injects extra fields onto your model, with de-normalized data populated on save * Template tags are provided for displaying the related data, forms for posting them, and views for handling form posts where applicable For a complete implementation reference, take a look at the built-in blog app :mod:`mezzanine.blog` which makes use of all these. Lastly, some of the :ref:`templatetags` found within :mod:`mezzanine.core.templatetags.mezzanine_tags` are covered. .. _keywords: Keywords ======== Keywords provided by the :mod:`mezzanine.generic` app are pervasive throughout Mezzanine. They're assigned to both the :class:`.Page` model and the :class:`.Displayable` model from which it's derived. Given that these models form the foundation of most content within Mezzanine, more often than not you're dealing with models that are already using keywords. Suppose we have a regular Django model though, such as our ``Book`` example from the previous example in :doc:`content-architecture`:: from django.db import models from mezzanine.generic.fields import KeywordsField class Book(models.Model): author = models.ForeignKey("Author") cover = models.ImageField(upload_to="authors") keywords = KeywordsField() When editing ``Book`` instances in the admin, we'll now be able to choose keywords from the pool of keywords used throughout the site, and also assign new keywords if needed. We can then easily query for books given any keywords:: Book.objects.filter(keywords__keyword__title__in=["eggs", "ham"]) Given a ``Book`` instance in a template, we can also display the book's keywords using the :func:`keywords_for` template tag, which will inject a list of keywords into the template, using the ``as var_name`` variable name argument supplied to it:: {% load keyword_tags %} {% keywords_for book as keywords %} {% if keywords %} {% endif %} You'll see here each ``Keyword`` instance has a slug field - we use it in a fictitious urlpattern called ``books_for_keyword``, which could then retrieve books for a given keyword by slug:: Book.objects.filter(keywords__keyword__slug=slug) Any model with a :class:`.KeywordsField` field assigned to it will have a ``FIELD_NAME_string`` field assigned to it, where ``FIELD_NAME`` is the name given to the :class:`.KeywordsField` attribute on your model, which would be ``Book.keywords_string`` in the above example. Each time keywords change, the ``keywords_string`` field is populated with a comma separated string list of each of the keywords. This can be used in conjunction with Mezzanine's :doc:`search-engine` - behavior that is provided by default for the :class:`.Page` and :class:`.Displayable` models. .. _comments: Threaded Comments ================= Threaded comments provided by the :mod:`mezzanine.generic` app are an extension of Django's `django_comments `_ app. Mezzanine's threaded comments fundamentally extend Django's comments to allow for threaded conversations, where comments can be made in reply to other comments. Again as with our ``Book`` example, suppose we wanted to add threaded conversations to our book pages in templates, we first define comments on the ``Book`` model:: from django.db import models from mezzanine.generic.fields import CommentsField class Book(models.Model): author = models.ForeignKey("Author") cover = models.ImageField(upload_to="authors") comments = CommentsField() Then given a ``Book`` instance named ``book`` in a template:: {% load comment_tags %}

There are {{ book.comments_count }} comment{{ book.comments_count|pluralize }}

{% comments_for book %} The ``comments_for`` template tag is a Django `inclusion tag `_, that includes the template ``generic/includes/comments.html``, which recursively includes the template ``generic/includes/comment.html`` to build up the threaded conversation. To customize the look and feel of the threaded conversation, simply override these templates in your project. As you can see in the template example we have a ``Book.comments_count`` field injected onto our ``Book`` model. This works the same way as described above for the :class:`.KeywordsField`, where the name is derived from the name given to the :class:`.CommentsField` attribute on the model, and updated each time the number of comments change. You can also require that users must be logged in to comment. This is controlled by setting the :ref:`COMMENTS_ACCOUNT_REQUIRED` setting to ``True``. In this case, the comment form will still be displayed, but on submitting a comment, the user will be redirected to the login/signup page, where after logging in, their comment will be posted without having to re-submit it. See the :doc:`user-accounts` section for full details on configuring public user accounts in Mezzanine. .. _ratings: Ratings ======= The ratings provided by the :mod:`mezzanine.generic` app allow people to give a rating for any model that has ratings set up. Suppose we wanted to allow people to rate our books from 1 to 10, first we define what the rating range is via the :ref:`RATINGS_RANGE` setting:: RATINGS_RANGE = range(1, 11) And then add ratings to our ``Book`` model:: from django.db import models from mezzanine.generic.fields import RatingField class Book(models.Model): author = models.ForeignKey("Author") cover = models.ImageField(upload_to="authors") rating = RatingField() And then in our book template:: {% load rating_tags %} {% rating_for book %} The :func:`rating_for` template tag is another inclusion tag, which uses the template ``generic/includes/rating.html``. It simply displays the current average rating, and a form with radio buttons for rating. You may wish to customize this and use visual icons, like stars, for the ratings. Like the other custom fields in :mod:`mezzanine.generic`, the :class:`.RatingField` will inject fields derived from its attribute name onto the model which it's assigned to, which are updated when a new rating is made. Given our ``Book`` example, the :class:`.RatingField` would inject: * ``Book.rating_average`` - average rating * ``Book.rating_sum`` - total sum of all ratings * ``Book.rating_count`` - total count of all ratings Like threaded comments, ratings can be limited to authenticated users by setting the :ref:`RATINGS_ACCOUNT_REQUIRED` setting to ``True``. .. _templatetags: General Template Tags ===================== Following are some template tags defined in :mod:`mezzanine.core.templatetags.mezzanine_tags` - they're general purpose and can be used across a variety of scenarios. :func:`.fields_for` ------------------- The :func:`.fields_for` template tag is a simple tag that takes a form object as its single argument, and renders the fields for the form. It uses the template ``core/templates/includes/form_fields.html``, which can then be overridden to customize the look and feel of all forms throughout a Mezzanine site:: {% load mezzanine_tags %}
{% fields_for some_form_object %}
:func:`.errors_for` ------------------- The :func:`.errors_for` template tag is an inclusion tag that takes a form object and renders any error messages with the template ``core/templates/includes/form_errors.html``. It plays well with :func:`.fields_for`:: {% load mezzanine_tags %} {% errors_for some_form_object %}
{% fields_for some_form_object %}
:func:`.sort_by` ---------------- The :func:`.sort_by` template tag is a general sorting utility. It's a filter tag similar to Django's `dictsort `_ filter tag, but instead of only accepting sequences of dicts and a key name, it also accepts sequences of objects and an attribute name, making it much more general purpose. Here's an example with the :func:`.keywords_for` tag described above, which assigns an :func:`.item_count` attribute to each keyword returned to the template:: {% load mezzanine_tags keywords_tags %} {% keywords_for book as keywords %} {% for keyword in keywords|sort_by:"item_count" %} ... etc ... {% endfor %} :func:`.thumbnail` ------------------ The :func:`.thumbnail` template tag provides on-the-fly image resizing. It takes the relative path to the image file to resize, and mandatory width and height arguments. When the :func:`.thumbnail` template tag is called for a given set of arguments the first time, the thumbnail is generated and its relative path is returned. Subsequent calls with the same arguments will return the same thumbnail path, without resizing it again, so resizes only occur when first requested. Given our book example's ``Book.cover`` field, suppose we wanted to render cover thumbnails with a 100 pixel width, and proportional height:: {% load mezzanine_tags %} The :func:`.thumbnail` template tag also accepts several other optional arguments for controlling the generated thumbnail: * ``upscale`` - A boolean controlling whether the thumbnail should grow beyond its original size when resizing (defaults to True) * ``quality`` - A value from 0 to 100 controlling the JPG quality (defaults to 95) * ``left`` and ``top`` - Values from 0 to 1 controlling where the image will be cropped (each defaults to 0.5, namely the center) * ``padding`` - A boolean controlling whether the thumbnail will be padded rather than cropped (defaults to False) * ``padding_color`` - RGB string controlling the background color when ``padding`` is True (defaults to "#fff")