Fields
======

.. _StatusField:

StatusField
-----------

A simple convenience for giving a model a set of "states."
``StatusField`` is a ``CharField`` subclass that expects to find a
class attribute called ``STATUS`` on its model or you can pass
``choices_name`` to use a different attribute name, and uses that as
its ``choices``. Also sets a default ``max_length`` of 100, and sets
its default value to the first item in the ``STATUS`` choices:

.. code-block:: python

    from model_utils.fields import StatusField
    from model_utils import Choices

    class Article(models.Model):
        STATUS = Choices('draft', 'published')
        # ...
        status = StatusField()

(The ``STATUS`` class attribute does not have to be a :ref:`Choices`
instance, it can be an ordinary list of two-tuples).

Using a different name for the model's choices class attribute

.. code-block:: python

    from model_utils.fields import StatusField
    from model_utils import Choices

    class Article(models.Model):
        ANOTHER_CHOICES = Choices('draft', 'published')
        # ...
        another_field = StatusField(choices_name='ANOTHER_CHOICES')

``StatusField`` does not set ``db_index=True`` automatically; if you
expect to frequently filter on your status field (and it will have
enough selectivity to make an index worthwhile) you may want to add this
yourself.


.. _MonitorField:

MonitorField
------------

A ``DateTimeField`` subclass that monitors another field on the model,
and updates itself to the current date-time whenever the monitored
field changes:

.. code-block:: python

    from model_utils.fields import MonitorField, StatusField

    class Article(models.Model):
        STATUS = Choices('draft', 'published')

        status = StatusField()
        status_changed = MonitorField(monitor='status')

(A ``MonitorField`` can monitor any type of field for changes, not only a
``StatusField``.)

If a list is passed to the ``when`` parameter, the field will only
update when it matches one of the specified values:

.. code-block:: python

    from model_utils.fields import MonitorField, StatusField

    class Article(models.Model):
        STATUS = Choices('draft', 'published')

        status = StatusField()
        published_at = MonitorField(monitor='status', when=['published'])


SplitField
----------

A ``TextField`` subclass that automatically pulls an excerpt out of
its content (based on a "split here" marker or a default number of
initial paragraphs) and stores both its content and excerpt values in
the database.

A ``SplitField`` is easy to add to any model definition:

.. code-block:: python

    from django.db import models
    from model_utils.fields import SplitField

    class Article(models.Model):
        title = models.CharField(max_length=100)
        body = SplitField()

``SplitField`` automatically creates an extra non-editable field
``_body_excerpt`` to store the excerpt. This field doesn't need to be
accessed directly; see below.


Accessing a SplitField on a model
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When accessing an attribute of a model that was declared as a
``SplitField``, a ``SplitText`` object is returned.  The ``SplitText``
object has three attributes:

``content``:
    The full field contents.
``excerpt``:
    The excerpt of ``content`` (read-only).
``has_more``:
    True if the excerpt and content are different, False otherwise.

This object also has a ``__unicode__`` method that returns the full
content, allowing ``SplitField`` attributes to appear in templates
without having to access ``content`` directly.

Assuming the ``Article`` model above:

.. code-block:: pycon

    >>> a = Article.objects.all()[0]
    >>> a.body.content
    u'some text\n\n<!-- split -->\n\nmore text'
    >>> a.body.excerpt
    u'some text\n'
    >>> unicode(a.body)
    u'some text\n\n<!-- split -->\n\nmore text'

Assignment to ``a.body`` is equivalent to assignment to
``a.body.content``.

.. note::

    a.body.excerpt is only updated when a.save() is called


Customized excerpting
~~~~~~~~~~~~~~~~~~~~~

By default, ``SplitField`` looks for the marker ``<!-- split -->``
alone on a line and takes everything before that marker as the
excerpt. This marker can be customized by setting the ``SPLIT_MARKER``
setting.

If no marker is found in the content, the first two paragraphs (where
paragraphs are blocks of text separated by a blank line) are taken to
be the excerpt. This number can be customized by setting the
``SPLIT_DEFAULT_PARAGRAPHS`` setting.


UUIDField
----------

A ``UUIDField`` subclass that provides an UUID field. You can
add this field to any model definition.

With the param ``primary_key`` you can set if this field is the
primary key for the model, default is True.

Param ``version`` is an integer that set default UUID version.
Versions 1,3,4 and 5 are supported, default is 4.

If ``editable`` is set to false the field will not be displayed in the admin
or any other ModelForm, default is False.


.. code-block:: python

    from django.db import models
    from model_utils.fields import UUIDField

    class MyAppModel(models.Model):
        uuid = UUIDField(primary_key=True, version=4, editable=False)


UrlsafeTokenField
-----------------

A ``CharField`` subclass that provides random token generating using
python's ``secrets.token_urlsafe`` as default value.

If ``editable`` is set to false the field will not be displayed in the admin
or any other ModelForm, default is False.

``max_length`` specifies the maximum length of the token. The default value is 128.


.. code-block:: python

    from django.db import models
    from model_utils.fields import UrlsafeTokenField


    class MyAppModel(models.Model):
        uuid = UrlsafeTokenField(editable=False, max_length=128)


You can provide your custom token generator using the ``factory`` argument.
``factory`` should be callable. It will raise ``TypeError`` if it is not callable.
``factory`` is called with ``max_length`` argument to generate the token, and should
return a string of specified maximum length.


.. code-block:: python

    import uuid

    from django.db import models
    from model_utils.fields import UrlsafeTokenField


    def _token_factory(max_length):
        return uuid.uuid4().hex


    class MyAppModel(models.Model):
        uuid = UrlsafeTokenField(max_length=32, factory=_token_factory)
