How it works...

In Python 3.4, a new Enum class was introduced, filling a gap in the core functionality, as compared to other languages. Enumerations, which are fixed sets of key-value pairs, are perfect for use when generating model field choices. Since there is a specific taxonomy for https://schema.org microdata itemtype and itemprop names, we can enumerate those available options. However, we can't simply use an Enum itself as the choices value, since that field must contain an iterable (list or tuple) where each element is itself an iterable of exactly two items. Instead, we create a ChoiceEnum subclass of Enum, with a choices() method that generates the tuple of 2-tuples needed by Django.

Another strength of the https://schema.org microdata is a rich taxonomy tree, where nested types inherit properties from their more generic parent types. Unfortunately, it is not inherently possible to have the same type of inheritance with Enum objects, which cannot be extended once they define properties. To add this functionality, we create another ItemPropChoiceEnum. This richer version of the ChoiceEnum supports a way to define parents() for the enumeration. The choices() logic is augmented to use this hierarchy to compose a union of all of the available choices for a given enumeration and its parents.

Now that we have the starting points, we will create a single list of values for use in itemtype attributes, and then several field-type-specific lists for itemprop attributes. There is some unavoidable duplication across the item property enumerations, because certain properties allow for very particular types, and others are less strict.

Note that the item type and item property enumerations shown here are far from exhaustive. The complete hierarchy of schema types can be found at https://schema.org/docs/full.html.

The last piece that we will add to our util is the SchemaMicrodata model mixin, which provides an itemtype field to any models that use it, similar to the metadata fields added in the Creating a model mixin to take care of meta tags recipe, earlier in this chapter. A convenient method is also provided, in order to generate a safe HTML snippet for the itemtype attribute, to be used in templates as follows:

<section {{ thing.itemtype_attribute }}>...</section>

Next, we will set up a receiver that acts on the class_prepared signal, which is triggered whenever a model is loaded and ready for use, and we will wire it up to be loaded when the application configuration is ready. The receiver checks the sender (a model) to see if it subclasses the SchemaMicrodata mixin that we just created, and finds the set of fields to be augmented with itemprop. If choices are available for the field's type (for example, CharFieldItemProp for a CharField), it is then paired with an autogenerated itemprop field, using those choices. The result might be something like the following:

class Idea(SchemaMicrodata):
    title = models.CharField(
        _("Title"),
        max_length=200)
title_itemprop = models.CharField(
_("Title microdata item property"),
name="title_itemprop",
max_length=200,
unique=False,
blank=True,
null=False,
default="",
editable=True,
choices=(("ACCESS_MODE", "accessMode"),
("ALTERNATE_NAME", "alternateName"),
("BOOK_EDITION", "bookEdition"),
("DESCRIPTION", "description"),
("HEADLINE", "headline")))

content = models.TextField( _("Content"), blank=True)
content_itemprop = models.TextField(
_("Content microdata item property"),
name="content_itemprop",
max_length=200,
unique=False,
blank=True,
null=False,
default="",
editable=True,
choices=(("ACCESS_MODE", "accessMode"),
("ALTERNATE_NAME", "alternateName"),
("BOOK_EDITION", "bookEdition"),
("DESCRIPTION", "description"),
("HEADLINE", "headline")))

Two templates are used to define how to represent the new microdata in the markup, and helper methods make use of these, so that we can easily provide the available microdata:

<section {{ idea.itemtype_attribute }}>
<header {{ idea.title_itemprop_attribute }}>
{{ idea.title }}
</header>
<div {{ idea.content_itemprop_attribute }}>
{{ idea.content }}
</div>
</section>

When evaluated, assuming that we have an itemtype (and only the itemprop for the title), we might see something like the following:

<section itemscope itemtype="//schema.org/CreativeWork">
<header itemprop="headline">
This is the Title
</header>
<div>
Content goes here...
</div>
</section>