From aa55457c8294aea809c5a41835097735a75def5c Mon Sep 17 00:00:00 2001 From: "LB (Ben Johnston)" Date: Thu, 21 Jul 2022 01:49:25 +1000 Subject: [PATCH] Convert reference pages documentation section to Markdown (#8881) Co-authored-by: Thibaud Colas --- docs/reference/contrib/sitemaps.rst | 2 +- docs/reference/pages/model_recipes.md | 277 +++++++++++++++++ docs/reference/pages/model_recipes.rst | 288 ------------------ ...model_reference.rst => model_reference.md} | 285 +++++++++-------- ...et_reference.rst => queryset_reference.md} | 42 ++- 5 files changed, 433 insertions(+), 461 deletions(-) create mode 100644 docs/reference/pages/model_recipes.md delete mode 100644 docs/reference/pages/model_recipes.rst rename docs/reference/pages/{model_reference.rst => model_reference.md} (85%) rename docs/reference/pages/{queryset_reference.rst => queryset_reference.md} (90%) diff --git a/docs/reference/contrib/sitemaps.rst b/docs/reference/contrib/sitemaps.rst index 369f991286..58d39fe6cc 100644 --- a/docs/reference/contrib/sitemaps.rst +++ b/docs/reference/contrib/sitemaps.rst @@ -89,7 +89,7 @@ a valid, crawlable hostname. If you change the site's hostname from If you change the site's port to ``443``, the ``https`` scheme will be used. -Find out more about :ref:`working with Sites`. +Find out more about :ref:`working with Sites`. Customising diff --git a/docs/reference/pages/model_recipes.md b/docs/reference/pages/model_recipes.md new file mode 100644 index 0000000000..9c04dec534 --- /dev/null +++ b/docs/reference/pages/model_recipes.md @@ -0,0 +1,277 @@ +(page_model_recipes)= + +# Recipes + +## Overriding the {meth}`~wagtail.models.Page.serve` Method + +Wagtail defaults to serving {class}`~wagtail.models.Page`-derived models by passing a reference to the page object to a Django HTML template matching the model's name, but suppose you wanted to serve something other than HTML? You can override the {meth}`~wagtail.models.Page.serve` method provided by the {class}`~wagtail.models.Page` class and handle the Django request and response more directly. + +Consider this example of an `EventPage` object which is served as an iCal file if the `format` variable is set in the request: + +```python +class EventPage(Page): + ... + + def serve(self, request): + if "format" in request.GET: + if request.GET['format'] == 'ical': + # Export to ical format + response = HttpResponse( + export_event(self, 'ical'), + content_type='text/calendar', + ) + response['Content-Disposition'] = 'attachment; filename=' + self.slug + '.ics' + return response + else: + # Unrecognised format error + message = 'Could not export event\n\nUnrecognised format: ' + request.GET['format'] + return HttpResponse(message, content_type='text/plain') + else: + # Display event page as usual + return super().serve(request) +``` + +{meth}`~wagtail.models.Page.serve` takes a Django request object and returns a Django response object. Wagtail returns a `TemplateResponse` object with the template and context which it generates, which allows middleware to function as intended, so keep in mind that a simpler response object like a `HttpResponse` will not receive these benefits. + +With this strategy, you could use Django or Python utilities to render your model in JSON or XML or any other format you'd like. + +(overriding_route_method)= + +### Adding Endpoints with Custom {meth}`~wagtail.models.Page.route` Methods + +```{note} +A much simpler way of adding more endpoints to pages is provided by the [](routable_page_mixin) mixin. +``` + +Wagtail routes requests by iterating over the path components (separated with a forward slash `/`), finding matching objects based on their slug, and delegating further routing to that object's model class. The Wagtail source is very instructive in figuring out what's happening. This is the default `route()` method of the `Page` class: + +```python +class Page(...): + ... + + def route(self, request, path_components): + if path_components: + # request is for a child of this page + child_slug = path_components[0] + remaining_components = path_components[1:] + + # find a matching child or 404 + try: + subpage = self.get_children().get(slug=child_slug) + except Page.DoesNotExist: + raise Http404 + + # delegate further routing + return subpage.specific.route(request, remaining_components) + + else: + # request is for this very page + if self.live: + # Return a RouteResult that will tell Wagtail to call + # this page's serve() method + return RouteResult(self) + else: + # the page matches the request, but isn't published, so 404 + raise Http404 +``` + +{meth}`~wagtail.models.Page.route` takes the current object (`self`), the `request` object, and a list of the remaining `path_components` from the request URL. It either continues delegating routing by calling {meth}`~wagtail.models.Page.route` again on one of its children in the Wagtail tree, or ends the routing process by returning a `RouteResult` object or raising a 404 error. + +The `RouteResult` object (defined in wagtail.url_routing) encapsulates all the information Wagtail needs to call a page's {meth}`~wagtail.models.Page.serve` method and return a final response: this information consists of the page object, and any additional `args`/`kwargs` to be passed to {meth}`~wagtail.models.Page.serve`. + +By overriding the {meth}`~wagtail.models.Page.route` method, we could create custom endpoints for each object in the Wagtail tree. One use case might be using an alternate template when encountering the `print/` endpoint in the path. Another might be a REST API which interacts with the current object. Just to see what's involved, lets make a simple model which prints out all of its child path components. + +First, `models.py`: + +```python +from django.shortcuts import render +from wagtail.url_routing import RouteResult +from django.http.response import Http404 +from wagtail.models import Page + +# ... + +class Echoer(Page): + + def route(self, request, path_components): + if path_components: + # tell Wagtail to call self.serve() with an additional 'path_components' kwarg + return RouteResult(self, kwargs={'path_components': path_components}) + else: + if self.live: + # tell Wagtail to call self.serve() with no further args + return RouteResult(self) + else: + raise Http404 + + def serve(self, path_components=[]): + return render(request, self.template, { + 'page': self, + 'echo': ' '.join(path_components), + }) +``` + +This model, `Echoer`, doesn't define any properties, but does subclass `Page` so objects will be able to have a custom title and slug. The template just has to display our `{{ echo }}` property. + +Now, once creating a new `Echoer` page in the Wagtail admin titled "Echo Base," requests such as: + +``` +http://127.0.0.1:8000/echo-base/tauntaun/kennel/bed/and/breakfast/ +``` + +Will return: + +``` +tauntaun kennel bed and breakfast +``` + +Be careful if you're introducing new required arguments to the `serve()` method - Wagtail still needs to be able to display a default view of the page for previewing and moderation, and by default will attempt to do this by calling `serve()` with a request object and no further arguments. If your `serve()` method does not accept that as a method signature, you will need to override the page's `serve_preview()` method to call `serve()` with suitable arguments: + +```python +def serve_preview(self, request, mode_name): + return self.serve(request, variant='radiant') +``` + +(tagging)= + +## Tagging + +Wagtail provides tagging capabilities through the combination of two Django modules, [django-taggit ](https://django-taggit.readthedocs.io/) (which provides a general-purpose tagging implementation) and [django-modelcluster](https://github.com/wagtail/django-modelcluster) (which extends django-taggit's `TaggableManager` to allow tag relations to be managed in memory without writing to the database - necessary for handling previews and revisions). To add tagging to a page model, you'll need to define a 'through' model inheriting from `TaggedItemBase` to set up the many-to-many relationship between django-taggit's `Tag` model and your page model, and add a `ClusterTaggableManager` accessor to your page model to present this relation as a single tag field. + +In this example, we set up tagging on `BlogPage` through a `BlogPageTag` model: + +```python +# models.py + +from modelcluster.fields import ParentalKey +from modelcluster.contrib.taggit import ClusterTaggableManager +from taggit.models import TaggedItemBase + +class BlogPageTag(TaggedItemBase): + content_object = ParentalKey('demo.BlogPage', on_delete=models.CASCADE, related_name='tagged_items') + +class BlogPage(Page): + ... + tags = ClusterTaggableManager(through=BlogPageTag, blank=True) + + promote_panels = Page.promote_panels + [ + ... + FieldPanel('tags'), + ] +``` + +Wagtail's admin provides a nice interface for inputting tags into your content, with typeahead tag completion and friendly tag icons. + +We can now make use of the many-to-many tag relationship in our views and templates. For example, we can set up the blog's index page to accept a `?tag=...` query parameter to filter the `BlogPage` listing by tag: + +```python +from django.shortcuts import render + +class BlogIndexPage(Page): + ... + def get_context(self, request): + context = super().get_context(request) + + # Get blog entries + blog_entries = BlogPage.objects.child_of(self).live() + + # Filter by tag + tag = request.GET.get('tag') + if tag: + blog_entries = blog_entries.filter(tags__name=tag) + + context['blog_entries'] = blog_entries + return context +``` + +Here, `blog_entries.filter(tags__name=tag)` follows the `tags` relation on `BlogPage`, to filter the listing to only those pages with a matching tag name before passing this to the template for rendering. We can now update the `blog_page.html` template to show a list of tags associated with the page, with links back to the filtered index page: + +```html+django +{% for tag in page.tags.all %} + {{ tag }} +{% endfor %} +``` + +Iterating through `page.tags.all` will display each tag associated with `page`, while the links back to the index make use of the filter option added to the `BlogIndexPage` model. A Django query could also use the `tagged_items` related name field to get `BlogPage` objects associated with a tag. + +The same approach can be used to add tagging to non-page models managed through [](snippets) and [](../contrib/modeladmin/index). In this case, the model must inherit from `modelcluster.models.ClusterableModel` to be compatible with `ClusterTaggableManager`. + +### Custom tag models + +In the above example, any newly-created tags will be added to django-taggit's default `Tag` model, which will be shared by all other models using the same recipe as well as Wagtail's image and document models. In particular, this means that the autocompletion suggestions on tag fields will include tags previously added to other models. To avoid this, you can set up a custom tag model inheriting from `TagBase`, along with a 'through' model inheriting from `ItemBase`, which will provide an independent pool of tags for that page model. + +```python +from django.db import models +from modelcluster.contrib.taggit import ClusterTaggableManager +from modelcluster.fields import ParentalKey +from taggit.models import TagBase, ItemBase + +class BlogTag(TagBase): + class Meta: + verbose_name = "blog tag" + verbose_name_plural = "blog tags" + + +class TaggedBlog(ItemBase): + tag = models.ForeignKey( + BlogTag, related_name="tagged_blogs", on_delete=models.CASCADE + ) + content_object = ParentalKey( + to='demo.BlogPage', + on_delete=models.CASCADE, + related_name='tagged_items' + ) + +class BlogPage(Page): + ... + tags = ClusterTaggableManager(through='demo.TaggedBlog', blank=True) +``` + +Within the admin, the tag field will automatically recognise the custom tag model being used, and will offer autocomplete suggestions taken from that tag model. + +### Disabling free tagging + +By default, tag fields work on a "free tagging" basis: editors can enter anything into the field, and upon saving, any tag text not recognised as an existing tag will be created automatically. To disable this behaviour, and only allow editors to enter tags that already exist in the database, custom tag models accept a `free_tagging = False` option: + +```python +from taggit.models import TagBase +from wagtail.snippets.models import register_snippet + +@register_snippet +class BlogTag(TagBase): + free_tagging = False + + class Meta: + verbose_name = "blog tag" + verbose_name_plural = "blog tags" +``` + +Here we have registered `BlogTag` as a snippet, to provide an interface for administrators (and other users with the appropriate permissions) to manage the allowed set of tags. With the `free_tagging = False` option set, editors can no longer enter arbitrary text into the tag field, and must instead select existing tags from the autocomplete dropdown. + +### Managing tags with Wagtail's `ModelAdmin` + +In order to manage all the tags used in a project, you can a use the `ModelAdmin` to add the `Tag` model to the Wagtail admin. This will allow you to have a tag admin interface within the main menu in which you can add, edit or delete your tags. + +Tags that are removed from a content don't get deleted from the `Tag` model and will still be shown in typeahead tag completion. So having a tag interface is a great way to completely get rid of tags you don't need. + +To add the tag interface, add the following block of code to a `wagtail_hooks.py` file within any your project’s apps: + +```python +from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register +from wagtail.admin.edit_handlers import FieldPanel +from taggit.models import Tag + + +class TagsModelAdmin(ModelAdmin): + Tag.panels = [FieldPanel("name")] # only show the name field + model = Tag + menu_label = "Tags" + menu_icon = "tag" # change as required + menu_order = 200 # will put in 3rd place (000 being 1st, 100 2nd) + list_display = ["name", "slug"] + search_fields = ("name",) + +modeladmin_register(TagsModelAdmin) +``` + +A `Tag` model has a `name` and `slug` required fields. If you decide to add a tag, it is recommended to only display the `name` field panel as the slug field is autofilled when the `name` field is filled and you don't need to enter the same name in both the fields. diff --git a/docs/reference/pages/model_recipes.rst b/docs/reference/pages/model_recipes.rst deleted file mode 100644 index 31c4a67dfa..0000000000 --- a/docs/reference/pages/model_recipes.rst +++ /dev/null @@ -1,288 +0,0 @@ - -.. _page_model_recipes: - -Recipes -======= - -Overriding the :meth:`~wagtail.models.Page.serve` Method --------------------------------------------------------------------- - -Wagtail defaults to serving :class:`~wagtail.models.Page`-derived models by passing a reference to the page object to a Django HTML template matching the model's name, but suppose you wanted to serve something other than HTML? You can override the :meth:`~wagtail.models.Page.serve` method provided by the :class:`~wagtail.models.Page` class and handle the Django request and response more directly. - -Consider this example of an ``EventPage`` object which is served as an iCal file if the ``format`` variable is set in the request: - -.. code-block:: python - - class EventPage(Page): - ... - - def serve(self, request): - if "format" in request.GET: - if request.GET['format'] == 'ical': - # Export to ical format - response = HttpResponse( - export_event(self, 'ical'), - content_type='text/calendar', - ) - response['Content-Disposition'] = 'attachment; filename=' + self.slug + '.ics' - return response - else: - # Unrecognised format error - message = 'Could not export event\n\nUnrecognised format: ' + request.GET['format'] - return HttpResponse(message, content_type='text/plain') - else: - # Display event page as usual - return super().serve(request) - -:meth:`~wagtail.models.Page.serve` takes a Django request object and returns a Django response object. Wagtail returns a ``TemplateResponse`` object with the template and context which it generates, which allows middleware to function as intended, so keep in mind that a simpler response object like a ``HttpResponse`` will not receive these benefits. - -With this strategy, you could use Django or Python utilities to render your model in JSON or XML or any other format you'd like. - - -.. _overriding_route_method: - -Adding Endpoints with Custom :meth:`~wagtail.models.Page.route` Methods ------------------------------------------------------------------------------------ - -.. note:: - - A much simpler way of adding more endpoints to pages is provided by the :mod:`~wagtail.contrib.routable_page` module. - -Wagtail routes requests by iterating over the path components (separated with a forward slash ``/``), finding matching objects based on their slug, and delegating further routing to that object's model class. The Wagtail source is very instructive in figuring out what's happening. This is the default ``route()`` method of the ``Page`` class: - -.. code-block:: python - - class Page(...): - ... - - def route(self, request, path_components): - if path_components: - # request is for a child of this page - child_slug = path_components[0] - remaining_components = path_components[1:] - - # find a matching child or 404 - try: - subpage = self.get_children().get(slug=child_slug) - except Page.DoesNotExist: - raise Http404 - - # delegate further routing - return subpage.specific.route(request, remaining_components) - - else: - # request is for this very page - if self.live: - # Return a RouteResult that will tell Wagtail to call - # this page's serve() method - return RouteResult(self) - else: - # the page matches the request, but isn't published, so 404 - raise Http404 - -:meth:`~wagtail.models.Page.route` takes the current object (``self``), the ``request`` object, and a list of the remaining ``path_components`` from the request URL. It either continues delegating routing by calling :meth:`~wagtail.models.Page.route` again on one of its children in the Wagtail tree, or ends the routing process by returning a ``RouteResult`` object or raising a 404 error. - -The ``RouteResult`` object (defined in wagtail.url_routing) encapsulates all the information Wagtail needs to call a page's :meth:`~wagtail.models.Page.serve` method and return a final response: this information consists of the page object, and any additional ``args``/``kwargs`` to be passed to :meth:`~wagtail.models.Page.serve`. - -By overriding the :meth:`~wagtail.models.Page.route` method, we could create custom endpoints for each object in the Wagtail tree. One use case might be using an alternate template when encountering the ``print/`` endpoint in the path. Another might be a REST API which interacts with the current object. Just to see what's involved, lets make a simple model which prints out all of its child path components. - -First, ``models.py``: - -.. code-block:: python - - from django.shortcuts import render - from wagtail.url_routing import RouteResult - from django.http.response import Http404 - from wagtail.models import Page - - ... - - class Echoer(Page): - - def route(self, request, path_components): - if path_components: - # tell Wagtail to call self.serve() with an additional 'path_components' kwarg - return RouteResult(self, kwargs={'path_components': path_components}) - else: - if self.live: - # tell Wagtail to call self.serve() with no further args - return RouteResult(self) - else: - raise Http404 - - def serve(self, path_components=[]): - return render(request, self.template, { - 'page': self, - 'echo': ' '.join(path_components), - }) - - -This model, ``Echoer``, doesn't define any properties, but does subclass ``Page`` so objects will be able to have a custom title and slug. The template just has to display our ``{{ echo }}`` property. - -Now, once creating a new ``Echoer`` page in the Wagtail admin titled "Echo Base," requests such as:: - - http://127.0.0.1:8000/echo-base/tauntaun/kennel/bed/and/breakfast/ - -Will return:: - - tauntaun kennel bed and breakfast - -Be careful if you're introducing new required arguments to the ``serve()`` method - Wagtail still needs to be able to display a default view of the page for previewing and moderation, and by default will attempt to do this by calling ``serve()`` with a request object and no further arguments. If your ``serve()`` method does not accept that as a method signature, you will need to override the page's ``serve_preview()`` method to call ``serve()`` with suitable arguments: - -.. code-block:: python - - def serve_preview(self, request, mode_name): - return self.serve(request, variant='radiant') - -.. _tagging: - -Tagging -------- - -Wagtail provides tagging capabilities through the combination of two Django modules, `django-taggit `_ (which provides a general-purpose tagging implementation) and `django-modelcluster `_ (which extends django-taggit's ``TaggableManager`` to allow tag relations to be managed in memory without writing to the database - necessary for handling previews and revisions). To add tagging to a page model, you'll need to define a 'through' model inheriting from ``TaggedItemBase`` to set up the many-to-many relationship between django-taggit's ``Tag`` model and your page model, and add a ``ClusterTaggableManager`` accessor to your page model to present this relation as a single tag field. - -In this example, we set up tagging on ``BlogPage`` through a ``BlogPageTag`` model: - -.. code-block:: python - - # models.py - - from modelcluster.fields import ParentalKey - from modelcluster.contrib.taggit import ClusterTaggableManager - from taggit.models import TaggedItemBase - - class BlogPageTag(TaggedItemBase): - content_object = ParentalKey('demo.BlogPage', on_delete=models.CASCADE, related_name='tagged_items') - - class BlogPage(Page): - ... - tags = ClusterTaggableManager(through=BlogPageTag, blank=True) - - promote_panels = Page.promote_panels + [ - ... - FieldPanel('tags'), - ] - -Wagtail's admin provides a nice interface for inputting tags into your content, with typeahead tag completion and friendly tag icons. - -We can now make use of the many-to-many tag relationship in our views and templates. For example, we can set up the blog's index page to accept a ``?tag=...`` query parameter to filter the ``BlogPage`` listing by tag: - -.. code-block:: python - - from django.shortcuts import render - - class BlogIndexPage(Page): - ... - def get_context(self, request): - context = super().get_context(request) - - # Get blog entries - blog_entries = BlogPage.objects.child_of(self).live() - - # Filter by tag - tag = request.GET.get('tag') - if tag: - blog_entries = blog_entries.filter(tags__name=tag) - - context['blog_entries'] = blog_entries - return context - - -Here, ``blog_entries.filter(tags__name=tag)`` follows the ``tags`` relation on ``BlogPage``, to filter the listing to only those pages with a matching tag name before passing this to the template for rendering. We can now update the ``blog_page.html`` template to show a list of tags associated with the page, with links back to the filtered index page: - -.. code-block:: html+django - - {% for tag in page.tags.all %} - {{ tag }} - {% endfor %} - -Iterating through ``page.tags.all`` will display each tag associated with ``page``, while the links back to the index make use of the filter option added to the ``BlogIndexPage`` model. A Django query could also use the ``tagged_items`` related name field to get ``BlogPage`` objects associated with a tag. - -The same approach can be used to add tagging to non-page models managed through :ref:`snippets` and :doc:`/reference/contrib/modeladmin/index`. In this case, the model must inherit from ``modelcluster.models.ClusterableModel`` to be compatible with ``ClusterTaggableManager``. - - -Custom tag models ------------------ - -In the above example, any newly-created tags will be added to django-taggit's default ``Tag`` model, which will be shared by all other models using the same recipe as well as Wagtail's image and document models. In particular, this means that the autocompletion suggestions on tag fields will include tags previously added to other models. To avoid this, you can set up a custom tag model inheriting from ``TagBase``, along with a 'through' model inheriting from ``ItemBase``, which will provide an independent pool of tags for that page model. - -.. code-block:: python - - from django.db import models - from modelcluster.contrib.taggit import ClusterTaggableManager - from modelcluster.fields import ParentalKey - from taggit.models import TagBase, ItemBase - - class BlogTag(TagBase): - class Meta: - verbose_name = "blog tag" - verbose_name_plural = "blog tags" - - - class TaggedBlog(ItemBase): - tag = models.ForeignKey( - BlogTag, related_name="tagged_blogs", on_delete=models.CASCADE - ) - content_object = ParentalKey( - to='demo.BlogPage', - on_delete=models.CASCADE, - related_name='tagged_items' - ) - - class BlogPage(Page): - ... - tags = ClusterTaggableManager(through='demo.TaggedBlog', blank=True) - -Within the admin, the tag field will automatically recognise the custom tag model being used, and will offer autocomplete suggestions taken from that tag model. - - -Disabling free tagging ----------------------- - -By default, tag fields work on a "free tagging" basis: editors can enter anything into the field, and upon saving, any tag text not recognised as an existing tag will be created automatically. To disable this behaviour, and only allow editors to enter tags that already exist in the database, custom tag models accept a ``free_tagging = False`` option: - -.. code-block:: python - - from taggit.models import TagBase - from wagtail.snippets.models import register_snippet - - @register_snippet - class BlogTag(TagBase): - free_tagging = False - - class Meta: - verbose_name = "blog tag" - verbose_name_plural = "blog tags" - -Here we have registered ``BlogTag`` as a snippet, to provide an interface for administrators (and other users with the appropriate permissions) to manage the allowed set of tags. With the ``free_tagging = False`` option set, editors can no longer enter arbitrary text into the tag field, and must instead select existing tags from the autocomplete dropdown. - -Managing tags with Wagtail's `ModelAdmin` ------------------------------------------ - -In order to manage all the tags used in a project, you can a use the ``ModelAdmin`` to add the ``Tag`` model to the Wagtail admin. This will allow you to have a tag admin interface within the main menu in which you can add, edit or delete your tags. - -Tags that are removed from a content don't get deleted from the ``Tag`` model and will still be shown in typeahead tag completion. So having a tag interface is a great way to completely get rid of tags you don't need. - -To add the tag interface, add the following block of code to a ``wagtail_hooks.py`` file within any your project’s apps: - -.. code-block:: python - - from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register - from wagtail.admin.edit_handlers import FieldPanel - from taggit.models import Tag - - - class TagsModelAdmin(ModelAdmin): - Tag.panels = [FieldPanel("name")] # only show the name field - model = Tag - menu_label = "Tags" - menu_icon = "tag" # change as required - menu_order = 200 # will put in 3rd place (000 being 1st, 100 2nd) - list_display = ["name", "slug"] - search_fields = ("name",) - - - modeladmin_register(TagsModelAdmin) - - -A ``Tag`` model has a ``name`` and ``slug`` required fields. If you decide to add a tag, it is recommended to only display the ``name`` field panel as the slug field is autofilled when the ``name`` field is filled and you don't need to enter the same name in both the fields. diff --git a/docs/reference/pages/model_reference.rst b/docs/reference/pages/model_reference.md similarity index 85% rename from docs/reference/pages/model_reference.rst rename to docs/reference/pages/model_reference.md index 02922812de..c45be08c59 100644 --- a/docs/reference/pages/model_reference.rst +++ b/docs/reference/pages/model_reference.md @@ -1,19 +1,18 @@ -=============== -Model Reference -=============== +# Model Reference +```{eval-rst} .. automodule:: wagtail.models +``` -This document contains reference information for the model classes inside the ``wagtailcore`` module. +This document contains reference information for the model classes inside the `wagtailcore` module. -.. _page_model_ref: +(page_model_ref)= -``Page`` -======== +## `Page` -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: Page .. attribute:: title @@ -145,18 +144,17 @@ Database fields when a new page is created and copied when a translation of a page is made. A translation_key value can only be used on one page in each locale. +``` +### Methods and properties -Methods and properties -~~~~~~~~~~~~~~~~~~~~~~ - -In addition to the model fields provided, ``Page`` has many properties and methods that you may wish to reference, use, or override in creating your own models. - -.. note:: - - See also `django-treebeard `_'s `node API `_. ``Page`` is a subclass of `materialized path tree `_ nodes. +In addition to the model fields provided, `Page` has many properties and methods that you may wish to reference, use, or override in creating your own models. +```{note} +See also [django-treebeard](https://django-treebeard.readthedocs.io/en/latest/index.html)'s `node API [https://django-treebeard.readthedocs.io/en/latest/api.html](https://django-treebeard.readthedocs.io/en/latest/api.html). ``Page`` is a subclass of [materialized path tree](https://django-treebeard.readthedocs.io/en/latest/mp_tree.html) nodes. +``` +```{eval-rst} .. class:: Page :noindex: @@ -329,20 +327,19 @@ In addition to the model fields provided, ``Page`` has many properties and metho .. autoattribute:: current_workflow_task_state .. autoattribute:: current_workflow_task +``` -.. _site-model-ref: +(site_model_ref)= -``Site`` -======== +## `Site` -The ``Site`` model is useful for multi-site installations as it allows an administrator to configure which part of the tree to use for each hostname that the server responds on. +The `Site` model is useful for multi-site installations as it allows an administrator to configure which part of the tree to use for each hostname that the server responds on. -The :meth:`~wagtail.models.Site.find_for_request` function returns the Site object that will handle the given HTTP request. +The {meth}`~wagtail.models.Site.find_for_request` function returns the Site object that will handle the given HTTP request. +### Database fields -Database fields -~~~~~~~~~~~~~~~ - +```{eval-rst} .. class:: Site .. attribute:: hostname @@ -384,10 +381,11 @@ Database fields This is set to ``True`` if the site is the default. Only one site can be the default. The default site is used as a fallback in situations where a site with the required hostname/port couldn't be found. +``` -Methods and properties -~~~~~~~~~~~~~~~~~~~~~~ +### Methods and properties +```{eval-rst} .. class:: Site :noindex: @@ -404,34 +402,32 @@ Methods and properties - Everything else will use the ``http://`` scheme and the port will be appended to the end of the hostname (for example ``http://mysite.com:8000/``) .. automethod:: get_site_root_paths +``` +## Locale -Locale -====== +The `Locale` model defines the set of languages and/or locales that can be used on a site. +Each `Locale` record corresponds to a "language code" defined in the :ref:`wagtail_content_languages_setting` setting. -The ``Locale`` model defines the set of languages and/or locales that can be used on a site. -Each ``Locale`` record corresponds to a "language code" defined in the :ref:`wagtail_content_languages_setting` setting. +Wagtail will initially set up one `Locale` to act as the default language for all existing content. +This first locale will automatically pick the value from `WAGTAIL_CONTENT_LANGUAGES` that most closely matches the site primary language code defined in `LANGUAGE_CODE`. +If the primary language code is changed later, Wagtail will **not** automatically create a new `Locale` record or update an existing one. -Wagtail will initially set up one ``Locale`` to act as the default language for all existing content. -This first locale will automatically pick the value from ``WAGTAIL_CONTENT_LANGUAGES`` that most closely matches the site primary language code defined in ``LANGUAGE_CODE``. -If the primary language code is changed later, Wagtail will **not** automatically create a new ``Locale`` record or update an existing one. - -Before internationalisation is enabled, all pages use this primary ``Locale`` record. +Before internationalisation is enabled, all pages use this primary `Locale` record. This is to satisfy the database constraints, and makes it easier to switch internationalisation on at a later date. -Changing ``WAGTAIL_CONTENT_LANGUAGES`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +### Changing `WAGTAIL_CONTENT_LANGUAGES` -Languages can be added or removed from ``WAGTAIL_CONTENT_LANGUAGES`` over time. +Languages can be added or removed from `WAGTAIL_CONTENT_LANGUAGES` over time. -Before removing an option from ``WAGTAIL_CONTENT_LANGUAGES``, it's important that the ``Locale`` +Before removing an option from `WAGTAIL_CONTENT_LANGUAGES`, it's important that the `Locale` record is updated to a use a different content language or is deleted. -Any ``Locale`` instances that have invalid content languages are automatically filtered out from all +Any `Locale` instances that have invalid content languages are automatically filtered out from all database queries making them unable to be edited or viewed. -Methods and properties -~~~~~~~~~~~~~~~~~~~~~~ +### Methods and properties +```{eval-rst} .. class:: Locale :noindex: @@ -442,19 +438,18 @@ Methods and properties .. automethod:: get_active .. automethod:: get_display_name +``` +## Translatable Mixin -Translatable Mixin -================== - -``TranslatableMixin`` is an abstract model that can be added to any non-page Django model to make it translatable. +`TranslatableMixin` is an abstract model that can be added to any non-page Django model to make it translatable. Pages already include this mixin, so there is no need to add it. -Methods and properties -~~~~~~~~~~~~~~~~~~~~~~ +### Methods and properties -The ``locale`` and ``translation_key`` fields have a unique key constraint to prevent the object being translated into a language more than once. +The `locale` and `translation_key` fields have a unique key constraint to prevent the object being translated into a language more than once. +```{eval-rst} .. class:: TranslatableMixin :noindex: @@ -484,26 +479,25 @@ The ``locale`` and ``translation_key`` fields have a unique key constraint to pr .. automethod:: get_translation_model .. autoattribute:: localized +``` +(revision_model_ref)= -.. _revision_model_ref: +## `Revision` -``Revision`` -============ +Every time a page is edited, a new `Revision` is created and saved to the database. It can be used to find the full history of all changes that have been made to a page and it also provides a place for new changes to be kept before going live. -Every time a page is edited, a new ``Revision`` is created and saved to the database. It can be used to find the full history of all changes that have been made to a page and it also provides a place for new changes to be kept before going live. +- Revisions can be created from any {class}`~wagtail.models.Page` object by calling its {meth}`~Page.save_revision` method +- The content of the page is JSON-serialisable and stored in the {attr}`~Revision.content` field +- You can retrieve a `Revision` as a {class}`~wagtail.models.Page` object by calling the {meth}`~Revision.as_object` method -- Revisions can be created from any :class:`~wagtail.models.Page` object by calling its :meth:`~Page.save_revision` method -- The content of the page is JSON-serialisable and stored in the :attr:`~Revision.content` field -- You can retrieve a ``Revision`` as a :class:`~wagtail.models.Page` object by calling the :meth:`~Revision.as_object` method +```{versionchanged} 4.0 +The model has been renamed from ``PageRevision`` to ``Revision`` and it now references the ``Page`` model using a {class}`~django.contrib.contenttypes.fields.GenericForeignKey`. +``` -.. versionchanged:: 4.0 - - The model has been renamed from ``PageRevision`` to ``Revision`` and it now references the ``Page`` model using a :class:`~django.contrib.contenttypes.fields.GenericForeignKey`. - -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: Revision .. attribute:: content_object @@ -558,10 +552,11 @@ Database fields The field has been renamed from ``content_json`` to ``content`` and it now uses :class:`~django.db.models.JSONField` instead of :class:`~django.db.models.TextField`. +``` -Managers -~~~~~~~~ +### Managers +```{eval-rst} .. class:: Revision :noindex: @@ -599,10 +594,11 @@ Managers .. code-block:: python Revision.submitted_revisions.all() +``` -Methods and properties -~~~~~~~~~~~~~~~~~~~~~~ +### Methods and properties +```{eval-rst} .. class:: Revision :noindex: @@ -633,13 +629,13 @@ Methods and properties .. autoattribute:: base_content_object This property returns the object this revision belongs to as an instance of the base class. +``` -``GroupPagePermission`` -======================= +## `GroupPagePermission` -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: GroupPagePermission .. attribute:: group @@ -653,13 +649,13 @@ Database fields .. attribute:: permission_type (choice list) +``` -``PageViewRestriction`` -======================= +## `PageViewRestriction` -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: PageViewRestriction .. attribute:: page @@ -669,28 +665,27 @@ Database fields .. attribute:: password (text) +``` -``Orderable`` (abstract) -======================== +## `Orderable` (abstract) -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: Orderable .. attribute:: sort_order (number) +``` - -``Workflow`` -============ +## `Workflow` Workflows represent sequences of tasks which much be approved for an action to be performed on a page - typically publication. -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: Workflow .. attribute:: name @@ -704,11 +699,11 @@ Database fields (boolean) Whether or not the workflow is active: active workflows can be added to pages, and started. Inactive workflows cannot. +``` +### Methods and properties -Methods and properties -~~~~~~~~~~~~~~~~~~~~~~ - +```{eval-rst} .. class:: Workflow :noindex: @@ -719,16 +714,15 @@ Methods and properties .. automethod:: deactivate .. automethod:: all_pages +``` - -``WorkflowState`` -================= +## `WorkflowState` Workflow states represent the status of a started workflow on a page. -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: WorkflowState .. attribute:: page @@ -766,10 +760,11 @@ Database fields (foreign key to ``TaskState``) The ``TaskState`` model for the task the workflow is currently at: either completing (if in progress) or the final task state (if finished) +``` -Methods and properties -~~~~~~~~~~~~~~~~~~~~~~ +### Methods and properties +```{eval-rst} .. class:: WorkflowState :noindex: @@ -793,16 +788,15 @@ Methods and properties .. automethod:: all_tasks_with_status .. automethod:: revisions +``` - -``Task`` -======== +## `Task` Tasks represent stages in a workflow which must be approved for the workflow to complete successfully. -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: Task .. attribute:: name @@ -823,11 +817,11 @@ Database fields (foreign key to ``django.contrib.contenttypes.models.ContentType``) A foreign key to the :class:`~django.contrib.contenttypes.models.ContentType` object that represents the specific model of this task. +``` +### Methods and properties -Methods and properties -~~~~~~~~~~~~~~~~~~~~~~ - +```{eval-rst} .. class:: Task :noindex: @@ -866,16 +860,15 @@ Methods and properties .. automethod:: get_template_for_action .. automethod:: get_description +``` - -``TaskState`` -============= +## `TaskState` Task states store state information about the progress of a task on a particular page revision. -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: TaskState .. attribute:: workflow_state @@ -931,11 +924,11 @@ Database fields (text) A text comment, typically added by a user when the task is completed. +``` +### Methods and properties -Methods and properties -~~~~~~~~~~~~~~~~~~~~~~ - +```{eval-rst} .. class:: TaskState :noindex: @@ -961,15 +954,15 @@ Methods and properties .. automethod:: copy .. automethod:: get_comment +``` -``WorkflowTask`` -================ +## `WorkflowTask` Represents the ordering of a task in a specific workflow. -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: WorkflowTask .. attribute:: workflow @@ -985,15 +978,15 @@ Database fields (number) The ordering of the task in the workflow. +``` -``WorkflowPage`` -================ +## `WorkflowPage` Represents the assignment of a workflow to a page and its descendants. -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: WorkflowPage .. attribute:: workflow @@ -1003,15 +996,15 @@ Database fields .. attribute:: page (foreign key to ``Page``) +``` -``BaseLogEntry`` -================ +## `BaseLogEntry` An abstract base class that represents a record of an action performed on an object. -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: BaseLogEntry .. attribute:: content_type @@ -1069,10 +1062,11 @@ Database fields (boolean) A boolean that can set to ``True`` when the object is deleted. Used to filter entries in the Site History report. +``` -Methods and properties -~~~~~~~~~~~~~~~~~~~~~~ +### Methods and properties +```{eval-rst} .. class:: BaseLogEntry :noindex: @@ -1083,17 +1077,15 @@ Methods and properties .. autoattribute:: object_verbose_name .. automethod:: object_id +``` +## `PageLogEntry` +Represents a record of an action performed on an {class}`Page`, subclasses {class}`BaseLogEntry`. -``PageLogEntry`` -================ - -Represents a record of an action performed on an :class:`Page`, subclasses :class:`BaseLogEntry`. - -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: PageLogEntry .. attribute:: page @@ -1101,17 +1093,15 @@ Database fields (foreign key to :class:`Page`) A foreign key to the page the action is performed on. +``` - - -``Comment`` -=========== +## `Comment` Represents a comment on a page. -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: Comment .. attribute:: page @@ -1175,17 +1165,15 @@ Database fields (foreign key to user model) A foreign key to the user who resolved this comment, if any. +``` - - -``CommentReply`` -================ +## `CommentReply` Represents a reply to a comment thread. -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: CommentReply .. attribute:: comment @@ -1217,18 +1205,16 @@ Database fields (date/time) The date/time when the comment was updated. +``` - - -``PageSubscription`` -==================== +## `PageSubscription` Represents a user's subscription to email notifications about page events. Currently only used for comment notifications. -Database fields -~~~~~~~~~~~~~~~ +### Database fields +```{eval-rst} .. class:: PageSubscription .. attribute:: page @@ -1245,3 +1231,4 @@ Database fields Whether the user should receive comment notifications for all comments, or just comments in threads they participate in. +``` diff --git a/docs/reference/pages/queryset_reference.rst b/docs/reference/pages/queryset_reference.md similarity index 90% rename from docs/reference/pages/queryset_reference.rst rename to docs/reference/pages/queryset_reference.md index 41fee8f172..61615c7430 100644 --- a/docs/reference/pages/queryset_reference.rst +++ b/docs/reference/pages/queryset_reference.md @@ -1,36 +1,31 @@ -======================= -Page QuerySet reference -======================= +# Page QuerySet reference -All models that inherit from :class:`~wagtail.models.Page` are given some extra QuerySet methods accessible from their ``.objects`` attribute. +All models that inherit from {class}`~wagtail.models.Page` are given some extra QuerySet methods accessible from their `.objects` attribute. +## Examples -Examples -======== +### Selecting only live pages -- Selecting only live pages +```python +live_pages = Page.objects.live() +``` - .. code-block:: python +### Selecting published EventPages that are descendants of events_index - live_pages = Page.objects.live() +```python +events = EventPage.objects.live().descendant_of(events_index) +``` -- Selecting published EventPages that are descendants of events_index +### Getting a list of menu items - .. code-block:: python +```python +# This gets a QuerySet of live children of the homepage with ``show_in_menus`` set +menu_items = homepage.get_children().live().in_menu() +``` - events = EventPage.objects.live().descendant_of(events_index) - -- Getting a list of menu items - - .. code-block:: python - - # This gets a QuerySet of live children of the homepage with ``show_in_menus`` set - menu_items = homepage.get_children().live().in_menu() - - -Reference -========= +## Reference +```{eval-rst} .. automodule:: wagtail.query .. autoclass:: PageQuerySet @@ -285,3 +280,4 @@ Reference homepage.get_children().defer_streamfields().specific() .. automethod:: first_common_ancestor +```