diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 4dd9386632..51ddafdb21 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -5,7 +5,7 @@ Changelog ~~~~~~~~~~~~~~~~ * Add clarity to confirmation when being asked to convert an external link to an internal one (Thijs Kramer) - * Convert various pages in the documentation to Markdown (Khanh Hoang, Vu Pham, Daniel Kirkham, LB (Ben) Johnston) + * Convert various pages in the documentation to Markdown (Khanh Hoang, Vu Pham, Daniel Kirkham, LB (Ben) Johnston, Thiago Costa de Souza) * Add `base_url_path` to `ModelAdmin` so that the default URL structure of app_label/model_name can be overridden (Vu Pham, Khanh Hoang) * Add `full_url` to the API output of `ImageRenditionField` (Paarth Agarwal) * Fix issue where `ModelAdmin` index listings with export list enabled would show buttons with an incorrect layout (Josh Woodcock) diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index c8487cd772..8c72bfb689 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -598,6 +598,7 @@ Contributors * Josh Woodcock * Christian Franke * Tom Hu +* Thiago Costa de Souza Translators =========== diff --git a/docs/advanced_topics/api/index.rst b/docs/advanced_topics/api/index.md similarity index 57% rename from docs/advanced_topics/api/index.rst rename to docs/advanced_topics/api/index.md index 67e9066cdb..89b8240768 100644 --- a/docs/advanced_topics/api/index.rst +++ b/docs/advanced_topics/api/index.md @@ -1,17 +1,17 @@ -=========== -Wagtail API -=========== +# Wagtail API The API module provides a public-facing, JSON-formatted API to allow retrieving content as raw field data. This is useful for cases like serving content to non-web clients (such as a mobile phone app) or pulling content out of Wagtail for use in another site. -See `RFC 8: Wagtail API `_ +See [RFC 8: Wagtail API](https://github.com/wagtail/rfcs/blob/main/text/008-wagtail-api.md#12---stable-and-unstable-versions) for full details on our stabilisation policy. -.. toctree:: - :maxdepth: 2 - - v2/configuration - v2/usage +```{toctree} +--- +maxdepth: 2 +--- +v2/configuration +v2/usage +``` diff --git a/docs/advanced_topics/api/v2/configuration.md b/docs/advanced_topics/api/v2/configuration.md new file mode 100644 index 0000000000..9c3ed6db2f --- /dev/null +++ b/docs/advanced_topics/api/v2/configuration.md @@ -0,0 +1,271 @@ +(api_v2_configuration)= + +# Wagtail API v2 Configuration Guide + +This section of the docs will show you how to set up a public API for your +Wagtail site. + +Even though the API is built on Django REST Framework, you do not need to +install this manually as it is already a dependency of Wagtail. + +## Basic configuration + +### Enable the app + +Firstly, you need to enable Wagtail's API app so Django can see it. +Add `wagtail.api.v2` to `INSTALLED_APPS` in your Django project settings: + +```python +# settings.py + +INSTALLED_APPS = [ + ... + + 'wagtail.api.v2', + + ... +] +``` + +Optionally, you may also want to add `rest_framework` to `INSTALLED_APPS`. +This would make the API browsable when viewed from a web browser but is not +required for basic JSON-formatted output. + +### Configure endpoints + +Next, it's time to configure which content will be exposed on the API. Each +content type (such as pages, images and documents) has its own endpoint. +Endpoints are combined by a router, which provides the url configuration you +can hook into the rest of your project. + +Wagtail provides three endpoint classes you can use: + +```{eval-rst} +- Pages :class:`wagtail.api.v2.views.PagesAPIViewSet` +- Images :class:`wagtail.images.api.v2.views.ImagesAPIViewSet` +- Documents :class:`wagtail.documents.api.v2.views.DocumentsAPIViewSet` +``` + +You can subclass any of these endpoint classes to customize their functionality. +Additionally, there is a base endpoint class you can use for adding different +content types to the API: `wagtail.api.v2.views.BaseAPIViewSet` + +For this example, we will create an API that includes all three builtin content +types in their default configuration: + +```python +# api.py + +from wagtail.api.v2.views import PagesAPIViewSet +from wagtail.api.v2.router import WagtailAPIRouter +from wagtail.images.api.v2.views import ImagesAPIViewSet +from wagtail.documents.api.v2.views import DocumentsAPIViewSet + +# Create the router. "wagtailapi" is the URL namespace +api_router = WagtailAPIRouter('wagtailapi') + +# Add the three endpoints using the "register_endpoint" method. +# The first parameter is the name of the endpoint (eg. pages, images). This +# is used in the URL of the endpoint +# The second parameter is the endpoint class that handles the requests +api_router.register_endpoint('pages', PagesAPIViewSet) +api_router.register_endpoint('images', ImagesAPIViewSet) +api_router.register_endpoint('documents', DocumentsAPIViewSet) +``` + +Next, register the URLs so Django can route requests into the API: + +```python +# urls.py + +from .api import api_router + +urlpatterns = [ + ... + + path('api/v2/', api_router.urls), + + ... + + # Ensure that the api_router line appears above the default Wagtail page serving route + re_path(r'^', include(wagtail_urls)), +] +``` + +With this configuration, pages will be available at `/api/v2/pages/`, images +at `/api/v2/images/` and documents at `/api/v2/documents/` + +(apiv2_page_fields_configuration)= + +### Adding custom page fields + +It's likely that you would need to export some custom fields over the API. This +can be done by adding a list of fields to be exported into the `api_fields` +attribute for each page model. + +For example: + +```python +# blog/models.py + +from wagtail.api import apifield + +class blogpageauthor(orderable): + page = models.foreignkey('blog.blogpage', on_delete=models.cascade, related_name='authors') + name = models.charfield(max_length=255) + + api_fields = [ + apifield('name'), + ] + + +class blogpage(page): + published_date = models.datetimefield() + body = richtextfield() + feed_image = models.foreignkey('wagtailimages.image', on_delete=models.set_null, null=true, ...) + private_field = models.charfield(max_length=255) + + # export fields over the api + api_fields = [ + apifield('published_date'), + apifield('body'), + apifield('feed_image'), + apifield('authors'), # this will nest the relevant blogpageauthor objects in the api response + ] +``` + +This will make `published_date`, `body`, `feed_image` and a list of +`authors` with the `name` field available in the API. But to access these +fields, you must select the `blog.BlogPage` type using the `?type` +[parameter in the API itself](apiv2_custom_page_fields). + +### Custom serializers + +[Serializers](https://www.django-rest-framework.org/api-guide/fields) are used to convert the database representation of a model into +JSON format. You can override the serializer for any field using the +`serializer` keyword argument: + +```python +from rest_framework.fields import DateField + +class BlogPage(Page): + ... + + api_fields = [ + # Change the format of the published_date field to "Thursday 06 April 2017" + APIField('published_date', serializer=DateField(format='%A %d %B %Y')), + ... + ] +``` + +Django REST framework's serializers can all take a [source](https://www.django-rest-framework.org/api-guide/fields/#source) argument allowing you +to add API fields that have a different field name or no underlying field at all: + +```python +from rest_framework.fields import DateField + +class BlogPage(Page): + ... + + api_fields = [ + # Date in ISO8601 format (the default) + APIField('published_date'), + + # A separate published_date_display field with a different format + APIField('published_date_display', serializer=DateField(format='%A %d %B %Y', source='published_date')), + ... + ] +``` + +This adds two fields to the API (other fields omitted for brevity): + +```json +{ + "published_date": "2017-04-06", + "published_date_display": "Thursday 06 April 2017" +} +``` + +### Images in the API + +The `ImageRenditionField` serializer +allows you to add renditions of images into your API. It requires an image +filter string specifying the resize operations to perform on the image. It can +also take the `source` keyword argument described above. + +For example: + +```python +from wagtail.api import APIField +from wagtail.images.api.fields import ImageRenditionField + +class BlogPage(Page): + ... + + api_fields = [ + # Adds information about the source image (eg, title) into the API + APIField('feed_image'), + + # Adds a URL to a rendered thumbnail of the image to the API + APIField('feed_image_thumbnail', serializer=ImageRenditionField('fill-100x100', source='feed_image')), + ... + ] +``` + +This would add the following to the JSON: + +```json +{ + "feed_image": { + "id": 45529, + "meta": { + "type": "wagtailimages.Image", + "detail_url": "http://www.example.com/api/v2/images/12/", + "download_url": "/media/images/a_test_image.jpg", + "tags": [] + }, + "title": "A test image", + "width": 2000, + "height": 1125 + }, + "feed_image_thumbnail": { + "url": "/media/images/a_test_image.fill-100x100.jpg", + "full_url": "http://www.example.com/media/images/a_test_image.fill-100x100.jpg", + "width": 100, + "height": 100, + "alt": "image alt text" + } +} +``` + +Note: `download_url` is the original uploaded file path, whereas +`feed_image_thumbnail['url']` is the url of the rendered image. +When you are using another storage backend, such as S3, `download_url` will return +a URL to the image if your media files are properly configured. + +## Additional settings + +### `WAGTAILAPI_BASE_URL` + +(required when using frontend cache invalidation) + +This is used in two places, when generating absolute URLs to document files and +invalidating the cache. + +Generating URLs to documents will fall back the the current request's hostname +if this is not set. Cache invalidation cannot do this, however, so this setting +must be set when using this module alongside the `wagtailfrontendcache` module. + +### `WAGTAILAPI_SEARCH_ENABLED` + +(default: True) + +Setting this to false will disable full text search. This applies to all +endpoints. + +### `WAGTAILAPI_LIMIT_MAX` + +(default: 20) + +This allows you to change the maximum number of results a user can request at a +time. This applies to all endpoints. Set to `None` for no limit. diff --git a/docs/advanced_topics/api/v2/configuration.rst b/docs/advanced_topics/api/v2/configuration.rst deleted file mode 100644 index 566652f261..0000000000 --- a/docs/advanced_topics/api/v2/configuration.rst +++ /dev/null @@ -1,285 +0,0 @@ -.. _api_v2_configuration: - -================================== -Wagtail API v2 Configuration Guide -================================== - -This section of the docs will show you how to set up a public API for your -Wagtail site. - -Even though the API is built on Django REST Framework, you do not need to -install this manually as it is already a dependency of Wagtail. - -Basic configuration -=================== - -Enable the app --------------- - -Firstly, you need to enable Wagtail's API app so Django can see it. -Add ``wagtail.api.v2`` to ``INSTALLED_APPS`` in your Django project settings: - -.. code-block:: python - - # settings.py - - INSTALLED_APPS = [ - ... - - 'wagtail.api.v2', - - ... - ] - -Optionally, you may also want to add ``rest_framework`` to ``INSTALLED_APPS``. -This would make the API browsable when viewed from a web browser but is not -required for basic JSON-formatted output. - -Configure endpoints -------------------- - -Next, it's time to configure which content will be exposed on the API. Each -content type (such as pages, images and documents) has its own endpoint. -Endpoints are combined by a router, which provides the url configuration you -can hook into the rest of your project. - -Wagtail provides three endpoint classes you can use: - -- Pages :class:`wagtail.api.v2.views.PagesAPIViewSet` -- Images :class:`wagtail.images.api.v2.views.ImagesAPIViewSet` -- Documents :class:`wagtail.documents.api.v2.views.DocumentsAPIViewSet` - -You can subclass any of these endpoint classes to customise their functionality. -Additionally, there is a base endpoint class you can use for adding different -content types to the API: :class:`wagtail.api.v2.views.BaseAPIViewSet` - -For this example, we will create an API that includes all three builtin content -types in their default configuration: - -.. code-block:: python - - # api.py - - from wagtail.api.v2.views import PagesAPIViewSet - from wagtail.api.v2.router import WagtailAPIRouter - from wagtail.images.api.v2.views import ImagesAPIViewSet - from wagtail.documents.api.v2.views import DocumentsAPIViewSet - - # Create the router. "wagtailapi" is the URL namespace - api_router = WagtailAPIRouter('wagtailapi') - - # Add the three endpoints using the "register_endpoint" method. - # The first parameter is the name of the endpoint (eg. pages, images). This - # is used in the URL of the endpoint - # The second parameter is the endpoint class that handles the requests - api_router.register_endpoint('pages', PagesAPIViewSet) - api_router.register_endpoint('images', ImagesAPIViewSet) - api_router.register_endpoint('documents', DocumentsAPIViewSet) - -Next, register the URLs so Django can route requests into the API: - -.. code-block:: python - - # urls.py - - from .api import api_router - - urlpatterns = [ - ... - - path('api/v2/', api_router.urls), - - ... - - # Ensure that the api_router line appears above the default Wagtail page serving route - re_path(r'^', include(wagtail_urls)), - ] - -With this configuration, pages will be available at ``/api/v2/pages/``, images -at ``/api/v2/images/`` and documents at ``/api/v2/documents/`` - -.. _apiv2_page_fields_configuration: - -Adding custom page fields -------------------------- - -It's likely that you would need to export some custom fields over the API. This -can be done by adding a list of fields to be exported into the ``api_fields`` -attribute for each page model. - -For example: - -.. code-block:: python - - # blog/models.py - - from wagtail.api import APIField - - class BlogPageAuthor(Orderable): - page = models.ForeignKey('blog.BlogPage', on_delete=models.CASCADE, related_name='authors') - name = models.CharField(max_length=255) - - api_fields = [ - APIField('name'), - ] - - - class BlogPage(Page): - published_date = models.DateTimeField() - body = RichTextField() - feed_image = models.ForeignKey('wagtailimages.Image', on_delete=models.SET_NULL, null=True, ...) - private_field = models.CharField(max_length=255) - - # Export fields over the API - api_fields = [ - APIField('published_date'), - APIField('body'), - APIField('feed_image'), - APIField('authors'), # This will nest the relevant BlogPageAuthor objects in the API response - ] - -This will make ``published_date``, ``body``, ``feed_image`` and a list of -``authors`` with the ``name`` field available in the API. But to access these -fields, you must select the ``blog.BlogPage`` type using the ``?type`` -:ref:`parameter in the API itself `. - -Custom serialisers ------------------- - -Serialisers_ are used to convert the database representation of a model into -JSON format. You can override the serialiser for any field using the -``serializer`` keyword argument: - -.. code-block:: python - - from rest_framework.fields import DateField - - class BlogPage(Page): - ... - - api_fields = [ - # Change the format of the published_date field to "Thursday 06 April 2017" - APIField('published_date', serializer=DateField(format='%A %d %B %Y')), - ... - ] - -Django REST framework's serializers can all take a source_ argument allowing you -to add API fields that have a different field name or no underlying field at all: - -.. code-block:: python - - from rest_framework.fields import DateField - - class BlogPage(Page): - ... - - api_fields = [ - # Date in ISO8601 format (the default) - APIField('published_date'), - - # A separate published_date_display field with a different format - APIField('published_date_display', serializer=DateField(format='%A %d %B %Y', source='published_date')), - ... - ] - -This adds two fields to the API (other fields omitted for brevity): - -.. code-block:: json - - { - "published_date": "2017-04-06", - "published_date_display": "Thursday 06 April 2017" - } - -.. _Serialisers: https://www.django-rest-framework.org/api-guide/fields/ -.. _source: https://www.django-rest-framework.org/api-guide/fields/#source - -Images in the API ------------------ - -The :class:`~wagtail.images.api.fields.ImageRenditionField` serialiser -allows you to add renditions of images into your API. It requires an image -filter string specifying the resize operations to perform on the image. It can -also take the ``source`` keyword argument described above. - -For example: - -.. code-block:: python - - from wagtail.api import APIField - from wagtail.images.api.fields import ImageRenditionField - - class BlogPage(Page): - ... - - api_fields = [ - # Adds information about the source image (eg, title) into the API - APIField('feed_image'), - - # Adds a URL to a rendered thumbnail of the image to the API - APIField('feed_image_thumbnail', serializer=ImageRenditionField('fill-100x100', source='feed_image')), - ... - ] - -This would add the following to the JSON: - -.. code-block:: json - - { - "feed_image": { - "id": 45529, - "meta": { - "type": "wagtailimages.Image", - "detail_url": "http://www.example.com/api/v2/images/12/", - "download_url": "/media/images/a_test_image.jpg", - "tags": [] - }, - "title": "A test image", - "width": 2000, - "height": 1125 - }, - "feed_image_thumbnail": { - "url": "/media/images/a_test_image.fill-100x100.jpg", - "full_url": "http://www.example.com/media/images/a_test_image.fill-100x100.jpg", - "width": 100, - "height": 100, - "alt": "image alt text" - } - } - - -Note: ``download_url`` is the original uploaded file path, whereas -``feed_image_thumbnail['url']`` is the url of the rendered image. -When you are using another storage backend, such as S3, ``download_url`` will return -a URL to the image if your media files are properly configured. - -Additional settings -=================== - -``WAGTAILAPI_BASE_URL`` ------------------------ - -(required when using frontend cache invalidation) - -This is used in two places, when generating absolute URLs to document files and -invalidating the cache. - -Generating URLs to documents will fall back the the current request's hostname -if this is not set. Cache invalidation cannot do this, however, so this setting -must be set when using this module alongside the ``wagtailfrontendcache`` module. - -``WAGTAILAPI_SEARCH_ENABLED`` ------------------------------ - -(default: True) - -Setting this to false will disable full text search. This applies to all -endpoints. - -``WAGTAILAPI_LIMIT_MAX`` ------------------------- - -(default: 20) - -This allows you to change the maximum number of results a user can request at a -time. This applies to all endpoints. Set to ``None`` for no limit. diff --git a/docs/advanced_topics/api/v2/usage.md b/docs/advanced_topics/api/v2/usage.md new file mode 100644 index 0000000000..51f6bc3d08 --- /dev/null +++ b/docs/advanced_topics/api/v2/usage.md @@ -0,0 +1,603 @@ +# Wagtail API v2 Usage Guide + +The Wagtail API module exposes a public, read only, JSON-formatted API which +can be used by external clients (such as a mobile app) or the site's frontend. + +This document is intended for developers using the API exposed by Wagtail. For +documentation on how to enable the API module in your Wagtail site, see +[Wagtail API v2 Configuration Guide](/advanced_topics/api/v2/configuration) + +Contents + +```{contents} +--- +local: +depth: 3 +--- +``` + +## Fetching content + +To fetch content over the API, perform a `GET` request against one of the +following endpoints: + +- Pages `/api/v2/pages/` +- Images `/api/v2/images/` +- Documents `/api/v2/documents/` + +```{note} +The available endpoints and their URLs may vary from site to site, depending +on how the API has been configured. +``` + +### Example response + +Each response contains the list of items (`items`) and the total count +(`meta.total_count`). The total count is irrespective of pagination. + +```text +GET /api/v2/endpoint_name/ + +HTTP 200 OK +Content-Type: application/json + +{ + "meta": { + "total_count": "total number of results" + }, + "items": [ + { + "id": 1, + "meta": { + "type": "app_name.ModelName", + "detail_url": "http://api.example.com/api/v2/endpoint_name/1/" + }, + "field": "value" + }, + { + "id": 2, + "meta": { + "type": "app_name.ModelName", + "detail_url": "http://api.example.com/api/v2/endpoint_name/2/" + }, + "field": "different value" + } + ] +} +``` + +(apiv2_custom_page_fields)= + +### Custom page fields in the API + +Wagtail sites contain many page types, each with their own set of fields. The +`pages` endpoint will only expose the common fields by default (such as +`title` and `slug`). + +To access custom page fields with the API, select the page type with the +`?type` parameter. This will filter the results to only include pages of that +type but will also make all the exported custom fields for that type available +in the API. + +For example, to access the `published_date`, `body` and `authors` fields +on the `blog.BlogPage` model in the [configuration docs](apiv2_page_fields_configuration): + +``` +GET /api/v2/pages/?type=blog.BlogPage&fields=published_date,body,authors(name) + +HTTP 200 OK +Content-Type: application/json + +{ + "meta": { + "total_count": 10 + }, + "items": [ + { + "id": 1, + "meta": { + "type": "blog.BlogPage", + "detail_url": "http://api.example.com/api/v2/pages/1/", + "html_url": "http://www.example.com/blog/my-blog-post/", + "slug": "my-blog-post", + "first_published_at": "2016-08-30T16:52:00Z" + }, + "title": "Test blog post", + "published_date": "2016-08-30", + "authors": [ + { + "id": 1, + "meta": { + "type": "blog.BlogPageAuthor", + }, + "name": "Karl Hobley" + } + ] + }, + + ... + ] +} +``` + +```{note} +Only fields that have been explicitly exported by the developer may be used +in the API. This is done by adding a `api_fields` attribute to the page +model. You can read about configuration [here](apiv2_page_fields_configuration). +``` + +This doesn't apply to images/documents as there is only one model exposed in +those endpoints. But for projects that have customized image/document models, +the `api_fields` attribute can be used to export any custom fields into the +API. + +### Pagination + +The number of items in the response can be changed by using the `?limit` +parameter (default: 20) and the number of items to skip can be changed by using +the `?offset` parameter. + +For example: + +``` +GET /api/v2/pages/?offset=20&limit=20 + +HTTP 200 OK +Content-Type: application/json + +{ + "meta": { + "total_count": 50 + }, + "items": [ + pages 20 - 40 will be listed here. + ] +} +``` + +```{note} +There may be a maximum value for the `?limit` parameter. This can be +modified in your project settings by setting `WAGTAILAPI_LIMIT_MAX` to +either a number (the new maximum value) or `None` (which disables maximum +value check). +``` + +### Ordering + +The results can be ordered by any field by setting the `?order` parameter to +the name of the field to order by. + +``` +GET /api/v2/pages/?order=title + +HTTP 200 OK +Content-Type: application/json + +{ + "meta": { + "total_count": 50 + }, + "items": [ + pages will be listed here in ascending title order (a-z) + ] +} +``` + +The results will be ordered in ascending order by default. This can be changed +to descending order by prefixing the field name with a `-` sign. + +``` +GET /api/v2/pages/?order=-title + +HTTP 200 OK +Content-Type: application/json + +{ + "meta": { + "total_count": 50 + }, + "items": [ + pages will be listed here in descending title order (z-a) + ] +} +``` + +```{note} +Ordering is case-sensitive so lowercase letters are always ordered after +uppercase letters when in ascending order. +``` + +#### Random ordering + +Passing `random` into the `?order` parameter will make results return in a +random order. If there is no caching, each request will return results in a +different order. + +``` +GET /api/v2/pages/?order=random + +HTTP 200 OK +Content-Type: application/json + +{ + "meta": { + "total_count": 50 + }, + "items": [ + pages will be listed here in random order + ] +} +``` + +```{note} +It's not possible to use `?offset` while ordering randomly because +consistent random ordering cannot be guaranteed over multiple requests +(so requests for subsequent pages may return results that also appeared in +previous pages). +``` + +### Filtering + +Any field may be used in an exact match filter. Use the filter name as the +parameter and the value to match against. + +For example, to find a page with the slug "about": + +``` +GET /api/v2/pages/?slug=about + +HTTP 200 OK +Content-Type: application/json + +{ + "meta": { + "total_count": 1 + }, + "items": [ + { + "id": 10, + "meta": { + "type": "standard.StandardPage", + "detail_url": "http://api.example.com/api/v2/pages/10/", + "html_url": "http://www.example.com/about/", + "slug": "about", + "first_published_at": "2016-08-30T16:52:00Z" + }, + "title": "About" + }, + ] +} +``` + +(apiv2_filter_by_tree_position)= + +### Filtering by tree position (pages only) + +Pages can additionally be filtered by their relation to other pages in the tree. + +The `?child_of` filter takes the id of a page and filters the list of results +to contain only direct children of that page. + +For example, this can be useful for constructing the main menu, by passing the +id of the homepage to the filter: + +``` +GET /api/v2/pages/?child_of=2&show_in_menus=true + +HTTP 200 OK +Content-Type: application/json + +{ + "meta": { + "total_count": 5 + }, + "items": [ + { + "id": 3, + "meta": { + "type": "blog.BlogIndexPage", + "detail_url": "http://api.example.com/api/v2/pages/3/", + "html_url": "http://www.example.com/blog/", + "slug": "blog", + "first_published_at": "2016-09-21T13:54:00Z" + }, + "title": "About" + }, + { + "id": 10, + "meta": { + "type": "standard.StandardPage", + "detail_url": "http://api.example.com/api/v2/pages/10/", + "html_url": "http://www.example.com/about/", + "slug": "about", + "first_published_at": "2016-08-30T16:52:00Z" + }, + "title": "About" + }, + + ... + ] +} +``` + +The `?ancestor_of` filter takes the id of a page and filters the list +to only include ancestors of that page (parent, grandparent etc.) all the +way down to the site's root page. + +For example, when combined with the `type` filter it can be used to +find the particular `blog.BlogIndexPage` a `blog.BlogPage` belongs +to. By itself, it can be used to to construct a breadcrumb trail from +the current page back to the site's root page. + +The `?descendant_of` filter takes the id of a page and filter the list +to only include descendants of that page (children, grandchildren etc.). + +### Search + +Passing a query to the `?search` parameter will perform a full-text search on +the results. + +The query is split into "terms" (by word boundary), then each term is normalized +(lowercased and unaccented). + +For example: `?search=James+Joyce` + +#### Search operator + +The `search_operator` specifies how multiple terms in the query should be +handled. There are two possible values: + +- `and` - All terms in the search query (excluding stop words) must exist in + each result +- `or` - At least one term in the search query must exist in each result + +The `or` operator is generally better than `and` as it allows the user to be +inexact with their query and the ranking algorithm will make sure that +irrelevant results are not returned at the top of the page. + +The default search operator depends on whether the search engine being used by +the site supports ranking. If it does (Elasticsearch), the operator will default +to `or`. Otherwise (database), it will default to `and`. + +For the same reason, it's also recommended to use the `and` operator when +using `?search` in conjunction with `?order` (as this disables ranking). + +For example: `?search=James+Joyce&order=-first_published_at&search_operator=and` + +(apiv2_i18n_filters)= + +### Special filters for internationalized sites + +When `WAGTAIL_I18N_ENABLED` is set to `True` (see +[](enabling_internationalisation) for more details) two new filters are made +available on the pages endpoint. + +#### Filtering pages by locale + +The `?locale=` filter is used to filter the listing to only include pages in +the specified locale. For example: + +``` +GET /api/v2/pages/?locale=en-us + +HTTP 200 OK +Content-Type: application/json + +{ + "meta": { + "total_count": 5 + }, + "items": [ + { + "id": 10, + "meta": { + "type": "standard.StandardPage", + "detail_url": "http://api.example.com/api/v2/pages/10/", + "html_url": "http://www.example.com/usa-page/", + "slug": "usa-page", + "first_published_at": "2016-08-30T16:52:00Z", + "locale": "en-us" + }, + "title": "American page" + }, + + ... + ] +} +``` + +#### Getting translations of a page + +The `?translation_of` filter is used to filter the listing to only include +pages that are a translation of the specified page ID. For example: + +``` +GET /api/v2/pages/?translation_of=10 + +HTTP 200 OK +Content-Type: application/json + +{ + "meta": { + "total_count": 2 + }, + "items": [ + { + "id": 11, + "meta": { + "type": "standard.StandardPage", + "detail_url": "http://api.example.com/api/v2/pages/11/", + "html_url": "http://www.example.com/gb-page/", + "slug": "gb-page", + "first_published_at": "2016-08-30T16:52:00Z", + "locale": "en-gb" + }, + "title": "British page" + }, + { + "id": 12, + "meta": { + "type": "standard.StandardPage", + "detail_url": "http://api.example.com/api/v2/pages/12/", + "html_url": "http://www.example.com/fr-page/", + "slug": "fr-page", + "first_published_at": "2016-08-30T16:52:00Z", + "locale": "fr" + }, + "title": "French page" + }, + ] +} +``` + +### Fields + +By default, only a subset of the available fields are returned in the response. +The `?fields` parameter can be used to both add additional fields to the +response and remove default fields that you know you won't need. + +#### Additional fields + +Additional fields can be added to the response by setting `?fields` to a +comma-separated list of field names you want to add. + +For example, `?fields=body,feed_image` will add the `body` and `feed_image` +fields to the response. + +This can also be used across relationships. For example, +`?fields=body,feed_image(width,height)` will nest the `width` and `height` +of the image in the response. + +#### All fields + +Setting `?fields` to an asterisk (`*`) will add all available fields to the +response. This is useful for discovering what fields have been exported. + +For example: `?fields=*` + +#### Removing fields + +Fields you know that you do not need can be removed by prefixing the name with a +`-` and adding it to `?fields`. + +For example, `?fields=-title,body` will remove `title` and add `body`. + +This can also be used with the asterisk. For example, `?fields=*,-body` +adds all fields except for `body`. + +#### Removing all default fields + +To specify exactly the fields you need, you can set the first item in fields to +an underscore (`_`) which removes all default fields. + +For example, `?fields=_,title` will only return the title field. + +### Detail views + +You can retrieve a single object from the API by appending its id to the end of +the URL. For example: + +- Pages `/api/v2/pages/1/` +- Images `/api/v2/images/1/` +- Documents `/api/v2/documents/1/` + +All exported fields will be returned in the response by default. You can use the +`?fields` parameter to customize which fields are shown. + +For example: `/api/v2/pages/1/?fields=_,title,body` will return just the +`title` and `body` of the page with the id of 1. + +(apiv2_finding_pages_by_path)= + +### Finding pages by HTML path + +You can find an individual page by its HTML path using the `/api/v2/pages/find/?html_path=` view. + +This will return either a `302` redirect response to that page's detail view, or a `404` not found response. + +For example: `/api/v2/pages/find/?html_path=/` always redirects to the homepage of the site + +## Default endpoint fields + +### Common fields + +These fields are returned by every endpoint. + +**`id` (number)** +The unique ID of the object + +```{note} +Except for page types, every other content type has its own id space +so you must combine this with the ``type`` field in order to get a +unique identifier for an object. +``` + +**`type` (string)** +The type of the object in `app_label.ModelName` format + +**`detail_url` (string)** +The URL of the detail view for the object + +### Pages + +**`title` (string)** +**`meta.slug` (string)** +**`meta.show_in_menus` (boolean)** +**`meta.seo_title` (string)** +**`meta.search_description` (string)** +**`meta.first_published_at` (date/time)** +These values are taken from their corresponding fields on the page + +**`meta.html_url` (string)** +If the site has an HTML frontend that's generated by Wagtail, this +field will be set to the URL of this page + +**`meta.parent`** +Nests some information about the parent page (only available on detail +views) + +**`meta.alias_of` (dictionary)** +If the page marked as an alias return original page id and full url + +### Images + +**`title` (string)** +The value of the image's title field. Within Wagtail, this is used in +the image's `alt` HTML attribute. + +**`width` (number)** +**`height` (number)** +The size of the original image file + +**`meta.tags` (list of strings)** +A list of tags associated with the image + +### Documents + +**`title` (string)** +The value of the document's title field + +**`meta.tags` (list of strings)** +A list of tags associated with the document + +**`meta.download_url` (string)** +A URL to the document file + +## Changes since v1 + +### Breaking changes + +- The results list in listing responses has been renamed to `items` (was previously either `pages`, `images` or `documents`) + +### Major features + +- The `fields` parameter has been improved to allow removing fields, adding all fields and customising nested fields + +### Minor features + +- `html_url`, `slug`, `first_published_at`, `expires_at` and `show_in_menus` fields have been added to the pages endpoint +- `download_url` field has been added to the documents endpoint +- Multiple page types can be specified in `type` parameter on pages endpoint +- `true` and `false` may now be used when filtering boolean fields +- `order` can now be used in conjunction with `search` +- `search_operator` parameter was added diff --git a/docs/advanced_topics/api/v2/usage.rst b/docs/advanced_topics/api/v2/usage.rst deleted file mode 100644 index 87a61730f2..0000000000 --- a/docs/advanced_topics/api/v2/usage.rst +++ /dev/null @@ -1,652 +0,0 @@ -========================== -Wagtail API v2 Usage Guide -========================== - -The Wagtail API module exposes a public, read only, JSON-formatted API which -can be used by external clients (such as a mobile app) or the site's frontend. - -This document is intended for developers using the API exposed by Wagtail. For -documentation on how to enable the API module in your Wagtail site, see -:doc:`/advanced_topics/api/v2/configuration` - -.. contents:: - -Fetching content -================ - -To fetch content over the API, perform a ``GET`` request against one of the -following endpoints: - -- Pages ``/api/v2/pages/`` -- Images ``/api/v2/images/`` -- Documents ``/api/v2/documents/`` - -.. note:: - - The available endpoints and their URLs may vary from site to site, depending - on how the API has been configured. - -Example response ----------------- - -Each response contains the list of items (``items``) and the total count -(``meta.total_count``). The total count is irrespective of pagination. - -.. code-block:: text - - GET /api/v2/endpoint_name/ - - HTTP 200 OK - Content-Type: application/json - - { - "meta": { - "total_count": "total number of results" - }, - "items": [ - { - "id": 1, - "meta": { - "type": "app_name.ModelName", - "detail_url": "http://api.example.com/api/v2/endpoint_name/1/" - }, - "field": "value" - }, - { - "id": 2, - "meta": { - "type": "app_name.ModelName", - "detail_url": "http://api.example.com/api/v2/endpoint_name/2/" - }, - "field": "different value" - } - ] - } - -.. _apiv2_custom_page_fields: - -Custom page fields in the API ------------------------------ - -Wagtail sites contain many page types, each with their own set of fields. The -``pages`` endpoint will only expose the common fields by default (such as -``title`` and ``slug``). - -To access custom page fields with the API, select the page type with the -``?type`` parameter. This will filter the results to only include pages of that -type but will also make all the exported custom fields for that type available -in the API. - -For example, to access the ``published_date``, ``body`` and ``authors`` fields -on the ``blog.BlogPage`` model in the :ref:`configuration docs `: - -.. code-block:: text - - GET /api/v2/pages/?type=blog.BlogPage&fields=published_date,body,authors(name) - - HTTP 200 OK - Content-Type: application/json - - { - "meta": { - "total_count": 10 - }, - "items": [ - { - "id": 1, - "meta": { - "type": "blog.BlogPage", - "detail_url": "http://api.example.com/api/v2/pages/1/", - "html_url": "http://www.example.com/blog/my-blog-post/", - "slug": "my-blog-post", - "first_published_at": "2016-08-30T16:52:00Z" - }, - "title": "Test blog post", - "published_date": "2016-08-30", - "authors": [ - { - "id": 1, - "meta": { - "type": "blog.BlogPageAuthor", - }, - "name": "Karl Hobley" - } - ] - }, - - ... - ] - } - -.. note:: - - Only fields that have been explicitly exported by the developer may be used - in the API. This is done by adding a ``api_fields`` attribute to the page - model. You can read about configuration :ref:`here `. - -This doesn't apply to images/documents as there is only one model exposed in -those endpoints. But for projects that have customised image/document models, -the ``api_fields`` attribute can be used to export any custom fields into the -API. - -Pagination ----------- - -The number of items in the response can be changed by using the ``?limit`` -parameter (default: 20) and the number of items to skip can be changed by using -the ``?offset`` parameter. - -For example: - -.. code-block:: text - - GET /api/v2/pages/?offset=20&limit=20 - - HTTP 200 OK - Content-Type: application/json - - { - "meta": { - "total_count": 50 - }, - "items": [ - pages 20 - 40 will be listed here. - ] - } - -.. note:: - - There may be a maximum value for the ``?limit`` parameter. This can be - modified in your project settings by setting ``WAGTAILAPI_LIMIT_MAX`` to - either a number (the new maximum value) or ``None`` (which disables maximum - value check). - -Ordering --------- - -The results can be ordered by any field by setting the ``?order`` parameter to -the name of the field to order by. - -.. code-block:: text - - GET /api/v2/pages/?order=title - - HTTP 200 OK - Content-Type: application/json - - { - "meta": { - "total_count": 50 - }, - "items": [ - pages will be listed here in ascending title order (a-z) - ] - } - -The results will be ordered in ascending order by default. This can be changed -to descending order by prefixing the field name with a ``-`` sign. - -.. code-block:: text - - GET /api/v2/pages/?order=-title - - HTTP 200 OK - Content-Type: application/json - - { - "meta": { - "total_count": 50 - }, - "items": [ - pages will be listed here in descending title order (z-a) - ] - } - -.. note:: - - Ordering is case-sensitive so lowercase letters are always ordered after - uppercase letters when in ascending order. - -Random ordering -^^^^^^^^^^^^^^^ - -Passing ``random`` into the ``?order`` parameter will make results return in a -random order. If there is no caching, each request will return results in a -different order. - -.. code-block:: text - - GET /api/v2/pages/?order=random - - HTTP 200 OK - Content-Type: application/json - - { - "meta": { - "total_count": 50 - }, - "items": [ - pages will be listed here in random order - ] - } - -.. note:: - - It's not possible to use ``?offset`` while ordering randomly because - consistent random ordering cannot be guaranteed over multiple requests - (so requests for subsequent pages may return results that also appeared in - previous pages). - -Filtering ---------- - -Any field may be used in an exact match filter. Use the filter name as the -parameter and the value to match against. - -For example, to find a page with the slug "about": - -.. code-block:: text - - GET /api/v2/pages/?slug=about - - HTTP 200 OK - Content-Type: application/json - - { - "meta": { - "total_count": 1 - }, - "items": [ - { - "id": 10, - "meta": { - "type": "standard.StandardPage", - "detail_url": "http://api.example.com/api/v2/pages/10/", - "html_url": "http://www.example.com/about/", - "slug": "about", - "first_published_at": "2016-08-30T16:52:00Z" - }, - "title": "About" - }, - ] - } - -.. _apiv2_filter_by_tree_position: - -Filtering by tree position (pages only) ---------------------------------------- - -Pages can additionally be filtered by their relation to other pages in the tree. - -The ``?child_of`` filter takes the id of a page and filters the list of results -to contain only direct children of that page. - -For example, this can be useful for constructing the main menu, by passing the -id of the homepage to the filter: - -.. code-block:: text - - GET /api/v2/pages/?child_of=2&show_in_menus=true - - HTTP 200 OK - Content-Type: application/json - - { - "meta": { - "total_count": 5 - }, - "items": [ - { - "id": 3, - "meta": { - "type": "blog.BlogIndexPage", - "detail_url": "http://api.example.com/api/v2/pages/3/", - "html_url": "http://www.example.com/blog/", - "slug": "blog", - "first_published_at": "2016-09-21T13:54:00Z" - }, - "title": "About" - }, - { - "id": 10, - "meta": { - "type": "standard.StandardPage", - "detail_url": "http://api.example.com/api/v2/pages/10/", - "html_url": "http://www.example.com/about/", - "slug": "about", - "first_published_at": "2016-08-30T16:52:00Z" - }, - "title": "About" - }, - - ... - ] - } - -The ``?ancestor_of`` filter takes the id of a page and filters the list -to only include ancestors of that page (parent, grandparent etc.) all the -way down to the site's root page. - -For example, when combined with the ``type`` filter it can be used to -find the particular ``blog.BlogIndexPage`` a ``blog.BlogPage`` belongs -to. By itself, it can be used to to construct a breadcrumb trail from -the current page back to the site's root page. - -The ``?descendant_of`` filter takes the id of a page and filter the list -to only include descendants of that page (children, grandchildren etc.). - -Search ------- - -Passing a query to the ``?search`` parameter will perform a full-text search on -the results. - -The query is split into "terms" (by word boundary), then each term is normalised -(lowercased and unaccented). - -For example: ``?search=James+Joyce`` - -Search operator -^^^^^^^^^^^^^^^ - -The ``search_operator`` specifies how multiple terms in the query should be -handled. There are two possible values: - -- ``and`` - All terms in the search query (excluding stop words) must exist in - each result -- ``or`` - At least one term in the search query must exist in each result - -The ``or`` operator is generally better than ``and`` as it allows the user to be -inexact with their query and the ranking algorithm will make sure that -irrelevant results are not returned at the top of the page. - -The default search operator depends on whether the search engine being used by -the site supports ranking. If it does (Elasticsearch), the operator will default -to ``or``. Otherwise (database), it will default to ``and``. - -For the same reason, it's also recommended to use the ``and`` operator when -using ``?search`` in conjunction with ``?order`` (as this disables ranking). - -For example: ``?search=James+Joyce&order=-first_published_at&search_operator=and`` - -.. _apiv2_i18n_filters: - -Special filters for internationalised sites -------------------------------------------- - -When ``WAGTAIL_I18N_ENABLED`` is set to ``True`` (see -:ref:`enabling_internationalisation` for more details) two new filters are made -available on the pages endpoint. - -Filtering pages by locale -^^^^^^^^^^^^^^^^^^^^^^^^^ - -The ``?locale=`` filter is used to filter the listing to only include pages in -the specified locale. For example: - -.. code-block:: text - - GET /api/v2/pages/?locale=en-us - - HTTP 200 OK - Content-Type: application/json - - { - "meta": { - "total_count": 5 - }, - "items": [ - { - "id": 10, - "meta": { - "type": "standard.StandardPage", - "detail_url": "http://api.example.com/api/v2/pages/10/", - "html_url": "http://www.example.com/usa-page/", - "slug": "usa-page", - "first_published_at": "2016-08-30T16:52:00Z", - "locale": "en-us" - }, - "title": "American page" - }, - - ... - ] - } - -Getting translations of a page -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The ``?translation_of`` filter is used to filter the listing to only include -pages that are a translation of the specified page ID. For example: - -.. code-block:: text - - GET /api/v2/pages/?translation_of=10 - - HTTP 200 OK - Content-Type: application/json - - { - "meta": { - "total_count": 2 - }, - "items": [ - { - "id": 11, - "meta": { - "type": "standard.StandardPage", - "detail_url": "http://api.example.com/api/v2/pages/11/", - "html_url": "http://www.example.com/gb-page/", - "slug": "gb-page", - "first_published_at": "2016-08-30T16:52:00Z", - "locale": "en-gb" - }, - "title": "British page" - }, - { - "id": 12, - "meta": { - "type": "standard.StandardPage", - "detail_url": "http://api.example.com/api/v2/pages/12/", - "html_url": "http://www.example.com/fr-page/", - "slug": "fr-page", - "first_published_at": "2016-08-30T16:52:00Z", - "locale": "fr" - }, - "title": "French page" - }, - ] - } - -Fields ------- - -By default, only a subset of the available fields are returned in the response. -The ``?fields`` parameter can be used to both add additional fields to the -response and remove default fields that you know you won't need. - -Additional fields -^^^^^^^^^^^^^^^^^ - -Additional fields can be added to the response by setting ``?fields`` to a -comma-separated list of field names you want to add. - -For example, ``?fields=body,feed_image`` will add the ``body`` and ``feed_image`` -fields to the response. - -This can also be used across relationships. For example, -``?fields=body,feed_image(width,height)`` will nest the ``width`` and ``height`` -of the image in the response. - -All fields -^^^^^^^^^^ - -Setting ``?fields`` to an asterisk (``*``) will add all available fields to the -response. This is useful for discovering what fields have been exported. - -For example: ``?fields=*`` - -Removing fields -^^^^^^^^^^^^^^^ - -Fields you know that you do not need can be removed by prefixing the name with a -``-`` and adding it to ``?fields``. - -For example, ``?fields=-title,body`` will remove ``title`` and add ``body``. - -This can also be used with the asterisk. For example, ``?fields=*,-body`` -adds all fields except for ``body``. - -Removing all default fields -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -To specify exactly the fields you need, you can set the first item in fields to -an underscore (``_``) which removes all default fields. - -For example, ``?fields=_,title`` will only return the title field. - -Detail views ------------- - -You can retrieve a single object from the API by appending its id to the end of -the URL. For example: - -- Pages ``/api/v2/pages/1/`` -- Images ``/api/v2/images/1/`` -- Documents ``/api/v2/documents/1/`` - -All exported fields will be returned in the response by default. You can use the -``?fields`` parameter to customise which fields are shown. - -For example: ``/api/v2/pages/1/?fields=_,title,body`` will return just the -``title`` and ``body`` of the page with the id of 1. - - -.. _apiv2_finding_pages_by_path: - -Finding pages by HTML path --------------------------- - -You can find an individual page by its HTML path using the ``/api/v2/pages/find/?html_path=`` view. - -This will return either a ``302`` redirect response to that page's detail view, or a ``404`` not found response. - -For example: ``/api/v2/pages/find/?html_path=/`` always redirects to the homepage of the site - -Default endpoint fields -======================= - -Common fields -------------- - -These fields are returned by every endpoint. - -.. glossary:: - - ``id`` (number) - - The unique ID of the object - - .. note:: - - Except for page types, every other content type has its own id space - so you must combine this with the ``type`` field in order to get a - unique identifier for an object. - - ``type`` (string) - - The type of the object in ``app_label.ModelName`` format - - ``detail_url`` (string) - - The URL of the detail view for the object - -Pages ------ - -.. glossary:: - - ``title`` (string) - ``meta.slug`` (string) - ``meta.show_in_menus`` (boolean) - ``meta.seo_title`` (string) - ``meta.search_description`` (string) - ``meta.first_published_at`` (date/time) - - These values are taken from their corresponding fields on the page - - ``meta.html_url`` (string) - - If the site has an HTML frontend that's generated by Wagtail, this - field will be set to the URL of this page - - ``meta.parent`` - - Nests some information about the parent page (only available on detail - views) - - ``meta.alias_of`` (dictionary) - - If the page marked as an alias return original page id and full url - -Images ------- - -.. glossary:: - - ``title`` (string) - - The value of the image's title field. Within Wagtail, this is used in - the image's ``alt`` HTML attribute. - - ``width`` (number) - ``height`` (number) - - The size of the original image file - - ``meta.tags`` (list of strings) - - A list of tags associated with the image - -Documents ---------- - -.. glossary:: - - ``title`` (string) - - The value of the document's title field - - ``meta.tags`` (list of strings) - - A list of tags associated with the document - - ``meta.download_url`` (string) - - A URL to the document file - -Changes since v1 -================ - -Breaking changes ----------------- - -- The results list in listing responses has been renamed to ``items`` (was - previously either ``pages``, ``images`` or ``documents``) - -Major features --------------- - -- The ``fields`` parameter has been improved to allow removing fields, adding - all fields and customising nested fields - -Minor features --------------- - -- ``html_url``, ``slug``, ``first_published_at``, ``expires_at`` and - ``show_in_menus`` fields have been added to the pages endpoint -- ``download_url`` field has been added to the documents endpoint -- Multiple page types can be specified in ``type`` parameter on pages endpoint -- ``true`` and ``false`` may now be used when filtering boolean fields -- ``order`` can now be used in conjunction with ``search`` -- ``search_operator`` parameter was added diff --git a/docs/releases/4.0.md b/docs/releases/4.0.md index 2d94bab9c8..bef6288868 100644 --- a/docs/releases/4.0.md +++ b/docs/releases/4.0.md @@ -12,7 +12,7 @@ depth: 1 ### Other features * Add clarity to confirmation when being asked to convert an external link to an internal one (Thijs Kramer) - * Convert various pages in the documentation to Markdown (Khanh Hoang, Vu Pham, Daniel Kirkham, LB (Ben) Johnston) + * Convert various pages in the documentation to Markdown (Khanh Hoang, Vu Pham, Daniel Kirkham, LB (Ben) Johnston, Thiago Costa de Souza) * Add `base_url_path` to `ModelAdmin` so that the default URL structure of app_label/model_name can be overridden (Vu Pham, Khanh Hoang) * Add `full_url` to the API output of `ImageRenditionField` (Paarth Agarwal) * Use `InlinePanel`'s label when available for field comparison label (Sandil Ranasinghe)