documentation - update reference/contrib content to markdown

pull/8777/head
LB Johnston 2022-06-30 16:32:02 +10:00 zatwierdzone przez LB (Ben Johnston)
rodzic 4169d959fc
commit 1849122e5f
8 zmienionych plików z 615 dodań i 715 usunięć

Wyświetl plik

@ -1,167 +1,149 @@
.. _frontend_cache_purging:
(frontend_cache_purging)=
Frontend cache invalidator
==========================
# Frontend cache invalidator
Many websites use a frontend cache such as Varnish, Squid, Cloudflare or CloudFront to gain extra performance. The downside of using a frontend cache though is that they don't respond well to updating content and will often keep an old version of a page cached after it has been updated.
This document describes how to configure Wagtail to purge old versions of pages from a frontend cache whenever a page gets updated.
## Setting it up
Setting it up
-------------
Firstly, add `"wagtail.contrib.frontend_cache"` to your `INSTALLED_APPS`:
Firstly, add ``"wagtail.contrib.frontend_cache"`` to your INSTALLED_APPS:
```python
INSTALLED_APPS = [
...
.. code-block:: python
"wagtail.contrib.frontend_cache"
]
```
INSTALLED_APPS = [
...
The `wagtailfrontendcache` module provides a set of signal handlers which will automatically purge the cache whenever a page is published or deleted. These signal handlers are automatically registered when the `wagtail.contrib.frontend_cache` app is loaded.
"wagtail.contrib.frontend_cache"
]
### Varnish/Squid
The ``wagtailfrontendcache`` module provides a set of signal handlers which will automatically purge the cache whenever a page is published or deleted. These signal handlers are automatically registered when the ``wagtail.contrib.frontend_cache`` app is loaded.
Add a new item into the `WAGTAILFRONTENDCACHE` setting and set the `BACKEND` parameter to `wagtail.contrib.frontend_cache.backends.HTTPBackend`. This backend requires an extra parameter `LOCATION` which points to where the cache is running (this must be a direct connection to the server and cannot go through another proxy).
```python
# settings.py
Varnish/Squid
^^^^^^^^^^^^^
WAGTAILFRONTENDCACHE = {
'varnish': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.HTTPBackend',
'LOCATION': 'http://localhost:8000',
},
}
WAGTAILFRONTENDCACHE_LANGUAGES = []
```
Add a new item into the ``WAGTAILFRONTENDCACHE`` setting and set the ``BACKEND`` parameter to ``wagtail.contrib.frontend_cache.backends.HTTPBackend``. This backend requires an extra parameter ``LOCATION`` which points to where the cache is running (this must be a direct connection to the server and cannot go through another proxy).
.. code-block:: python
# settings.py
WAGTAILFRONTENDCACHE = {
'varnish': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.HTTPBackend',
'LOCATION': 'http://localhost:8000',
},
}
WAGTAILFRONTENDCACHE_LANGUAGES = []
Set ``WAGTAILFRONTENDCACHE_LANGUAGES`` to a list of languages (typically equal to ``[l[0] for l in settings.LANGUAGES]``) to also purge the urls for each language of a purging url. This setting needs ``settings.USE_I18N`` to be ``True`` to work. Its default is an empty list.
Set `WAGTAILFRONTENDCACHE_LANGUAGES` to a list of languages (typically equal to `[l[0] for l in settings.LANGUAGES]`) to also purge the urls for each language of a purging url. This setting needs `settings.USE_I18N` to be `True` to work. Its default is an empty list.
Finally, make sure you have configured your frontend cache to accept PURGE requests:
- `Varnish <https://www.varnish-cache.org/docs/3.0/tutorial/purging.html>`_
- `Squid <https://wiki.squid-cache.org/SquidFaq/OperatingSquid#How_can_I_purge_an_object_from_my_cache.3F>`_
- [Varnish](https://www.varnish-cache.org/docs/3.0/tutorial/purging.html)
- [Squid](https://wiki.squid-cache.org/SquidFaq/OperatingSquid#How_can_I_purge_an_object_from_my_cache.3F)
(frontendcache_cloudflare)=
.. _frontendcache_cloudflare:
### Cloudflare
Cloudflare
^^^^^^^^^^
Firstly, you need to register an account with Cloudflare if you haven't already got one. You can do this here: [Cloudflare Sign up](https://www.cloudflare.com/sign-up).
Firstly, you need to register an account with Cloudflare if you haven't already got one. You can do this here: `Cloudflare Sign up <https://www.cloudflare.com/sign-up>`_
Add an item into the ``WAGTAILFRONTENDCACHE`` and set the ``BACKEND`` parameter to ``wagtail.contrib.frontend_cache.backends.CloudflareBackend``.
Add an item into the `WAGTAILFRONTENDCACHE` and set the `BACKEND` parameter to `wagtail.contrib.frontend_cache.backends.CloudflareBackend`.
This backend can be configured to use an account-wide API key, or an API token with restricted access.
To use an account-wide API key, find the key `as described in the Cloudflare documentation <https://support.cloudflare.com/hc/en-us/articles/200167836-Managing-API-Tokens-and-Keys#12345682>`_ and specify ``EMAIL`` and ``API_KEY`` parameters.
To use an account-wide API key, find the key [as described in the Cloudflare documentation](https://support.cloudflare.com/hc/en-us/articles/200167836-Managing-API-Tokens-and-Keys#12345682) and specify `EMAIL` and `API_KEY` parameters.
To use a limited API token, `create a token <https://support.cloudflare.com/hc/en-us/articles/200167836-Managing-API-Tokens-and-Keys#12345680>`_ configured with the 'Zone, Cache Purge' permission and specify the ``BEARER_TOKEN`` parameter.
To use a limited API token, [create a token](https://support.cloudflare.com/hc/en-us/articles/200167836-Managing-API-Tokens-and-Keys#12345680) configured with the 'Zone, Cache Purge' permission and specify the `BEARER_TOKEN` parameter.
A ``ZONEID`` parameter will need to be set for either option. To find the ``ZONEID`` for your domain, read the `Cloudflare API Documentation <https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/>`_.
A `ZONEID` parameter will need to be set for either option. To find the `ZONEID` for your domain, read the [Cloudflare API Documentation](https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/find-account-and-zone-ids/).
With an API key:
.. code-block:: python
```python
# settings.py
# settings.py
WAGTAILFRONTENDCACHE = {
'cloudflare': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.CloudflareBackend',
'EMAIL': 'your-cloudflare-email-address@example.com',
'API_KEY': 'your cloudflare api key',
'ZONEID': 'your cloudflare domain zone id',
},
}
WAGTAILFRONTENDCACHE = {
'cloudflare': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.CloudflareBackend',
'EMAIL': 'your-cloudflare-email-address@example.com',
'API_KEY': 'your cloudflare api key',
'ZONEID': 'your cloudflare domain zone id',
},
}
```
With an API token:
.. code-block:: python
```python
# settings.py
# settings.py
WAGTAILFRONTENDCACHE = {
'cloudflare': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.CloudflareBackend',
'BEARER_TOKEN': 'your cloudflare bearer token',
'ZONEID': 'your cloudflare domain zone id',
},
}
```
WAGTAILFRONTENDCACHE = {
'cloudflare': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.CloudflareBackend',
'BEARER_TOKEN': 'your cloudflare bearer token',
'ZONEID': 'your cloudflare domain zone id',
},
}
(frontendcache_aws_cloudfront)=
.. _frontendcache_aws_cloudfront:
### Amazon CloudFront
Amazon CloudFront
^^^^^^^^^^^^^^^^^
Within Amazon Web Services you will need at least one CloudFront web distribution. If you don't have one, you can get one here: [CloudFront getting started](https://aws.amazon.com/cloudfront/)
Within Amazon Web Services you will need at least one CloudFront web distribution. If you don't have one, you can get one here: `CloudFront getting started <https://aws.amazon.com/cloudfront/>`_
Add an item into the `WAGTAILFRONTENDCACHE` and set the `BACKEND` parameter to `wagtail.contrib.frontend_cache.backends.CloudfrontBackend`. This backend requires one extra parameter, `DISTRIBUTION_ID` (your CloudFront generated distribution id).
Add an item into the ``WAGTAILFRONTENDCACHE`` and set the ``BACKEND`` parameter to ``wagtail.contrib.frontend_cache.backends.CloudfrontBackend``. This backend requires one extra parameter, ``DISTRIBUTION_ID`` (your CloudFront generated distribution id).
```python
WAGTAILFRONTENDCACHE = {
'cloudfront': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.CloudfrontBackend',
'DISTRIBUTION_ID': 'your-distribution-id',
},
}
```
.. code-block:: python
WAGTAILFRONTENDCACHE = {
'cloudfront': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.CloudfrontBackend',
'DISTRIBUTION_ID': 'your-distribution-id',
},
}
Configuration of credentials can done in multiple ways. You won't need to store them in your Django settings file. You can read more about this here: `Boto 3 Docs <https://boto3.readthedocs.org/en/latest/guide/configuration.html>`_
Configuration of credentials can done in multiple ways. You won't need to store them in your Django settings file. You can read more about this here: [Boto 3 Docs](https://boto3.readthedocs.org/en/latest/guide/configuration.html).
In case you run multiple sites with Wagtail and each site has its CloudFront distribution, provide a mapping instead of a single distribution. Make sure the mapping matches with the hostnames provided in your site settings.
.. code-block:: python
WAGTAILFRONTENDCACHE = {
'cloudfront': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.CloudfrontBackend',
'DISTRIBUTION_ID': {
'www.wagtail.org': 'your-distribution-id',
'www.madewithwagtail.org': 'your-distribution-id',
},
```python
WAGTAILFRONTENDCACHE = {
'cloudfront': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.CloudfrontBackend',
'DISTRIBUTION_ID': {
'www.wagtail.org': 'your-distribution-id',
'www.madewithwagtail.org': 'your-distribution-id',
},
}
},
}
```
.. note::
In most cases, absolute URLs with ``www`` prefixed domain names should be used in your mapping. Only drop the ``www`` prefix if you're absolutely sure you're not using it (for example a subdomain).
```{note}
In most cases, absolute URLs with ``www`` prefixed domain names should be used in your mapping. Only drop the ``www`` prefix if you're absolutely sure you're not using it (for example a subdomain).
```
Azure CDN
^^^^^^^^^
### Azure CDN
With `Azure CDN <https://azure.microsoft.com/en-gb/services/cdn/>`_ you will need a CDN profile with an endpoint configured.
.. _azure-mgmt-cdn: https://pypi.org/project/azure-mgmt-cdn/
.. _azure-identity: https://pypi.org/project/azure-identity/
.. _azure-mgmt-resource: https://pypi.org/project/azure-mgmt-resource/
With [Azure CDN](https://azure.microsoft.com/en-gb/services/cdn/) you will need a CDN profile with an endpoint configured.
The third-party dependencies of this backend are:
+-------------------------+-----------+---------------------------------------------------------------------------------------------------------------------------------------+
| PyPI Package | Essential | Reason |
+=========================+===========+=======================================================================================================================================+
| azure-mgmt-cdn_ | Yes | Interacting with the CDN service. |
+-------------------------+-----------+---------------------------------------------------------------------------------------------------------------------------------------+
| azure-identity_ | No | Obtaining credentials. It's optional if you want to specify your own credential using a ``CREDENTIALS`` setting (more details below). |
+-------------------------+-----------+---------------------------------------------------------------------------------------------------------------------------------------+
| azure-mgmt-resource_ | No | For obtaining the subscription ID. Redundant if you want to explicitly specify a ``SUBSCRIPTION_ID`` setting (more details below). |
+-------------------------+-----------+---------------------------------------------------------------------------------------------------------------------------------------+
| PyPI Package | Essential | Reason |
| ---------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| [`azure-mgmt-cdn`](https://pypi.org/project/azure-mgmt-cdn/) | Yes | Interacting with the CDN service. |
| [`azure-identity`](https://pypi.org/project/azure-identity/) | No | Obtaining credentials. It's optional if you want to specify your own credential using a `CREDENTIALS` setting (more details below). |
| [`azure-mgmt-resource`](https://pypi.org/project/azure-mgmt-resource/) | No | For obtaining the subscription ID. Redundant if you want to explicitly specify a `SUBSCRIPTION_ID` setting (more details below). |
Add an item into the ``WAGTAILFRONTENDCACHE`` and set the ``BACKEND`` parameter to ``wagtail.contrib.frontend_cache.backends.AzureCdnBackend``. This backend requires the following settings to be set:
Add an item into the `WAGTAILFRONTENDCACHE` and set the `BACKEND` parameter to `wagtail.contrib.frontend_cache.backends.AzureCdnBackend`. This backend requires the following settings to be set:
* ``RESOURCE_GROUP_NAME`` - the resource group that your CDN profile is in.
* ``CDN_PROFILE_NAME`` - the profile name of the CDN service that you want to use.
* ``CDN_ENDPOINT_NAME`` - the name of the endpoint you want to be purged.
.. code-block:: python
- `RESOURCE_GROUP_NAME` - the resource group that your CDN profile is in.
- `CDN_PROFILE_NAME` - the profile name of the CDN service that you want to use.
- `CDN_ENDPOINT_NAME` - the name of the endpoint you want to be purged.
```python
WAGTAILFRONTENDCACHE = {
'azure_cdn': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.AzureCdnBackend',
@ -170,180 +152,163 @@ Add an item into the ``WAGTAILFRONTENDCACHE`` and set the ``BACKEND`` parameter
'CDN_ENDPOINT_NAME': 'wagtailio-cdn-endpoint-123',
},
}
```
By default the credentials will use ``azure.identity.DefaultAzureCredential``. To modify the credential object used, please use ``CREDENTIALS`` setting. Read about your options on the `Azure documentation <https://docs.microsoft.com/en-us/azure/developer/python/azure-sdk-authenticate>`_.
By default the credentials will use `azure.identity.DefaultAzureCredential`. To modify the credential object used, please use `CREDENTIALS` setting. Read about your options on the [Azure documentation](https://docs.microsoft.com/en-us/azure/developer/python/azure-sdk-authenticate).
```python
from azure.common.credentials import ServicePrincipalCredentials
.. code-block:: python
WAGTAILFRONTENDCACHE = {
'azure_cdn': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.AzureCdnBackend',
'RESOURCE_GROUP_NAME': 'MY-WAGTAIL-RESOURCE-GROUP',
'CDN_PROFILE_NAME': 'wagtailio',
'CDN_ENDPOINT_NAME': 'wagtailio-cdn-endpoint-123',
'CREDENTIALS': ServicePrincipalCredentials(
client_id='your client id',
secret='your client secret',
)
},
}
```
from azure.common.credentials import ServicePrincipalCredentials
Another option that can be set is `SUBSCRIPTION_ID`. By default the first encountered subscription will be used, but if your credential has access to more subscriptions, you should set this to an explicit value.
WAGTAILFRONTENDCACHE = {
'azure_cdn': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.AzureCdnBackend',
'RESOURCE_GROUP_NAME': 'MY-WAGTAIL-RESOURCE-GROUP',
'CDN_PROFILE_NAME': 'wagtailio',
'CDN_ENDPOINT_NAME': 'wagtailio-cdn-endpoint-123',
'CREDENTIALS': ServicePrincipalCredentials(
client_id='your client id',
secret='your client secret',
)
},
}
### Azure Front Door
Another option that can be set is ``SUBSCRIPTION_ID``. By default the first encountered subscription will be used, but if your credential has access to more subscriptions, you should set this to an explicit value.
Azure Front Door
^^^^^^^^^^^^^^^^
With `Azure Front Door <https://azure.microsoft.com/en-gb/services/frontdoor/>`_ you will need a Front Door instance with caching enabled.
.. _azure-mgmt-frontdoor: https://pypi.org/project/azure-mgmt-frontdoor/
.. _azure-identity: https://pypi.org/project/azure-identity/
.. _azure-mgmt-resource: https://pypi.org/project/azure-mgmt-resource/
With [Azure Front Door](https://azure.microsoft.com/en-gb/services/frontdoor/) you will need a Front Door instance with caching enabled.
The third-party dependencies of this backend are:
+-------------------------+-----------+---------------------------------------------------------------------------------------------------------------------------------------+
| PyPI Package | Essential | Reason |
+=========================+===========+=======================================================================================================================================+
| azure-mgmt-frontdoor_ | Yes | Interacting with the Front Door service. |
+-------------------------+-----------+---------------------------------------------------------------------------------------------------------------------------------------+
| azure-identity_ | No | Obtaining credentials. It's optional if you want to specify your own credential using a ``CREDENTIALS`` setting (more details below). |
+-------------------------+-----------+---------------------------------------------------------------------------------------------------------------------------------------+
| azure-mgmt-resource_ | No | For obtaining the subscription ID. Redundant if you want to explicitly specify a ``SUBSCRIPTION_ID`` setting (more details below). |
+-------------------------+-----------+---------------------------------------------------------------------------------------------------------------------------------------+
| PyPI Package | Essential | Reason |
| ------------------------------------------------------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| [`azure-mgmt-frontdoor`](https://pypi.org/project/azure-mgmt-frontdoor/) | Yes | Interacting with the Front Door service. |
| [`azure-identity`](https://pypi.org/project/azure-identity/) | No | Obtaining credentials. It's optional if you want to specify your own credential using a `CREDENTIALS` setting (more details below). |
| [`azure-mgmt-resource`](https://pypi.org/project/azure-mgmt-resource/) | No | For obtaining the subscription ID. Redundant if you want to explicitly specify a `SUBSCRIPTION_ID` setting (more details below). |
Add an item into the ``WAGTAILFRONTENDCACHE`` and set the ``BACKEND`` parameter to ``wagtail.contrib.frontend_cache.backends.AzureFrontDoorBackend``. This backend requires the following settings to be set:
Add an item into the `WAGTAILFRONTENDCACHE` and set the `BACKEND` parameter to `wagtail.contrib.frontend_cache.backends.AzureFrontDoorBackend`. This backend requires the following settings to be set:
* ``RESOURCE_GROUP_NAME`` - the resource group that your Front Door instance is part of.
* ``FRONT_DOOR_NAME`` - your configured Front Door instance name.
- `RESOURCE_GROUP_NAME` - the resource group that your Front Door instance is part of.
- `FRONT_DOOR_NAME` - your configured Front Door instance name.
.. code-block:: python
```python
WAGTAILFRONTENDCACHE = {
'azure_front_door': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.AzureFrontDoorBackend',
'RESOURCE_GROUP_NAME': 'MY-WAGTAIL-RESOURCE-GROUP',
'FRONT_DOOR_NAME': 'wagtail-io-front-door',
},
}
```
WAGTAILFRONTENDCACHE = {
'azure_front_door': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.AzureFrontDoorBackend',
'RESOURCE_GROUP_NAME': 'MY-WAGTAIL-RESOURCE-GROUP',
'FRONT_DOOR_NAME': 'wagtail-io-front-door',
},
}
By default the credentials will use `azure.identity.DefaultAzureCredential`. To modify the credential object used, please use `CREDENTIALS` setting. Read about your options on the [Azure documentation](https://docs.microsoft.com/en-us/azure/developer/python/azure-sdk-authenticate).
By default the credentials will use ``azure.identity.DefaultAzureCredential``. To modify the credential object used, please use ``CREDENTIALS`` setting. Read about your options on the `Azure documentation <https://docs.microsoft.com/en-us/azure/developer/python/azure-sdk-authenticate>`_.
```python
from azure.common.credentials import ServicePrincipalCredentials
WAGTAILFRONTENDCACHE = {
'azure_front_door': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.AzureFrontDoorBackend',
'RESOURCE_GROUP_NAME': 'MY-WAGTAIL-RESOURCE-GROUP',
'FRONT_DOOR_NAME': 'wagtail-io-front-door',
'CREDENTIALS': ServicePrincipalCredentials(
client_id='your client id',
secret='your client secret',
)
},
}
```
.. code-block:: python
Another option that can be set is `SUBSCRIPTION_ID`. By default the first encountered subscription will be used, but if your credential has access to more subscriptions, you should set this to an explicit value.
from azure.common.credentials import ServicePrincipalCredentials
## Advanced usage
WAGTAILFRONTENDCACHE = {
'azure_front_door': {
'BACKEND': 'wagtail.contrib.frontend_cache.backends.AzureFrontDoorBackend',
'RESOURCE_GROUP_NAME': 'MY-WAGTAIL-RESOURCE-GROUP',
'FRONT_DOOR_NAME': 'wagtail-io-front-door',
'CREDENTIALS': ServicePrincipalCredentials(
client_id='your client id',
secret='your client secret',
)
},
}
### Invalidating more than one URL per page
Another option that can be set is ``SUBSCRIPTION_ID``. By default the first encountered subscription will be used, but if your credential has access to more subscriptions, you should set this to an explicit value.
By default, Wagtail will only purge one URL per page. If your page has more than one URL to be purged, you will need to override the `get_cached_paths` method on your page type.
Advanced usage
--------------
```python
class BlogIndexPage(Page):
def get_blog_items(self):
# This returns a Django paginator of blog items in this section
return Paginator(self.get_children().live().type(BlogPage), 10)
Invalidating more than one URL per page
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
def get_cached_paths(self):
# Yield the main URL
yield '/'
By default, Wagtail will only purge one URL per page. If your page has more than one URL to be purged, you will need to override the ``get_cached_paths`` method on your page type.
# Yield one URL per page in the paginator to make sure all pages are purged
for page_number in range(1, self.get_blog_items().num_pages + 1):
yield '/?page=' + str(page_number)
```
.. code-block:: python
class BlogIndexPage(Page):
def get_blog_items(self):
# This returns a Django paginator of blog items in this section
return Paginator(self.get_children().live().type(BlogPage), 10)
def get_cached_paths(self):
# Yield the main URL
yield '/'
# Yield one URL per page in the paginator to make sure all pages are purged
for page_number in range(1, self.get_blog_items().num_pages + 1):
yield '/?page=' + str(page_number)
Invalidating index pages
^^^^^^^^^^^^^^^^^^^^^^^^
### Invalidating index pages
Pages that list other pages (such as a blog index) may need to be purged as
well so any changes to a blog page are also reflected on the index (for example,
a blog post was added, deleted or its title/thumbnail was changed).
To purge these pages, we need to write a signal handler that listens for
Wagtail's ``page_published`` and ``page_unpublished`` signals for blog pages
(note, ``page_published`` is called both when a page is created and updated).
Wagtail's `page_published` and `page_unpublished` signals for blog pages
(note, `page_published` is called both when a page is created and updated).
This signal handler would trigger the invalidation of the index page using the
``PurgeBatch`` class which is used to construct and dispatch invalidation requests.
`PurgeBatch` class which is used to construct and dispatch invalidation requests.
.. code-block:: python
```python
# models.py
from django.dispatch import receiver
from django.db.models.signals import pre_delete
# models.py
from django.dispatch import receiver
from django.db.models.signals import pre_delete
from wagtail.signals import page_published
from wagtail.contrib.frontend_cache.utils import PurgeBatch
from wagtail.signals import page_published
from wagtail.contrib.frontend_cache.utils import PurgeBatch
...
...
def blog_page_changed(blog_page):
# Find all the live BlogIndexPages that contain this blog_page
batch = PurgeBatch()
for blog_index in BlogIndexPage.objects.live():
if blog_page in blog_index.get_blog_items().object_list:
batch.add_page(blog_index)
def blog_page_changed(blog_page):
# Find all the live BlogIndexPages that contain this blog_page
batch = PurgeBatch()
for blog_index in BlogIndexPage.objects.live():
if blog_page in blog_index.get_blog_items().object_list:
batch.add_page(blog_index)
# Purge all the blog indexes we found in a single request
batch.purge()
# Purge all the blog indexes we found in a single request
batch.purge()
@receiver(page_published, sender=BlogPage)
def blog_published_handler(instance, **kwargs):
blog_page_changed(instance)
@receiver(page_published, sender=BlogPage)
def blog_published_handler(instance, **kwargs):
blog_page_changed(instance)
@receiver(pre_delete, sender=BlogPage)
def blog_deleted_handler(instance, **kwargs):
blog_page_changed(instance)
@receiver(pre_delete, sender=BlogPage)
def blog_deleted_handler(instance, **kwargs):
blog_page_changed(instance)
```
(frontend_cache_invalidating_urls)=
.. _frontend_cache_invalidating_urls:
### Invalidating URLs
Invalidating URLs
^^^^^^^^^^^^^^^^^
The ``PurgeBatch`` class provides a ``.add_url(url)`` and a ``.add_urls(urls)``
The `PurgeBatch` class provides a `.add_url(url)` and a `.add_urls(urls)`
for adding individual URLs to the purge batch.
For example, this could be useful for purging a single page on a blog index:
.. code-block:: python
```python
from wagtail.contrib.frontend_cache.utils import PurgeBatch
from wagtail.contrib.frontend_cache.utils import PurgeBatch
# Purge the first page of the blog index
batch = PurgeBatch()
batch.add_url(blog_index.url + '?page=1')
batch.purge()
```
# Purge the first page of the blog index
batch = PurgeBatch()
batch.add_url(blog_index.url + '?page=1')
batch.purge()
### The `PurgeBatch` class
All of the methods available on `PurgeBatch` are listed below:
The ``PurgeBatch`` class
^^^^^^^^^^^^^^^^^^^^^^^^
All of the methods available on ``PurgeBatch`` are listed below:
```{eval-rst}
.. automodule:: wagtail.contrib.frontend_cache.utils
.. autoclass:: PurgeBatch
@ -356,3 +321,4 @@ All of the methods available on ``PurgeBatch`` are listed below:
.. automethod:: add_pages
.. automethod:: purge
```

Wyświetl plik

@ -1,94 +1,70 @@
Contrib modules
===============
# Contrib modules
Wagtail ships with a variety of extra optional modules.
```{toctree}
---
maxdepth: 2
---
settings
forms/index
sitemaps
frontendcache
routablepage
modeladmin/index
searchpromotions
simple_translation
table_block
typed_table_block
redirects
legacy_richtext
```
.. toctree::
:maxdepth: 2
settings
forms/index
sitemaps
frontendcache
routablepage
modeladmin/index
searchpromotions
simple_translation
table_block
typed_table_block
redirects
legacy_richtext
:doc:`settings`
---------------
## [](settings)
Settings that are editable by administrators within the Wagtail admin - either
site-specific or generic across all sites.
:doc:`forms/index`
------------------
## [](forms/index)
Allows forms to be created by admins and provides an interface for browsing form submissions.
:doc:`sitemaps`
---------------
## [](sitemaps)
Provides a view that generates a Google XML sitemap of your public Wagtail content.
:doc:`frontendcache`
--------------------
## [](frontendcache)
A module for automatically purging pages from a cache (Varnish, Squid, Cloudflare or Cloudfront) when their content is changed.
:doc:`routablepage`
-------------------
## [](routablepage)
Provides a way of embedding Django URLconfs into pages.
:doc:`modeladmin/index`
-----------------------
## [](modeladmin/index)
A module allowing for more customisable representation and management of custom models in Wagtail's admin area.
:doc:`searchpromotions`
-----------------------
## [](searchpromotions)
A module for managing "Promoted Search Results"
:doc:`simple_translation`
-------------------------
## [](simple_translation.md)
A module for copying translatables (pages and snippets) to another language.
:doc:`table_block`
-----------------------
## [](table_block)
Provides a TableBlock for adding HTML tables to pages.
:doc:`typed_table_block`
------------------------
## [](typed_table_block)
Provides a StreamField block for authoring tables, where cells can be any block type including rich text.
:doc:`redirects`
-----------------------
## [](redirects.md)
Provides a way to manage redirects.
## [](legacy_richtext)
:doc:`legacy_richtext`
-----------------------
Provides the legacy richtext wrapper (``<div class="rich-text"></div>``).
Provides the legacy richtext wrapper (`<div class="rich-text"></div>`).

Wyświetl plik

@ -1,185 +1,176 @@
.. _routable_page_mixin:
(routable_page_mixin)=
=====================
``RoutablePageMixin``
=====================
# `RoutablePageMixin`
```{eval-rst}
.. module:: wagtail.contrib.routable_page
```
The ``RoutablePageMixin`` mixin provides a convenient way for a page to respond on multiple sub-URLs with different views. For example, a blog section on a site might provide several different types of index page at URLs like ``/blog/2013/06/``, ``/blog/authors/bob/``, ``/blog/tagged/python/``, all served by the same page instance.
The `RoutablePageMixin` mixin provides a convenient way for a page to respond on multiple sub-URLs with different views. For example, a blog section on a site might provide several different types of index page at URLs like `/blog/2013/06/`, `/blog/authors/bob/`, `/blog/tagged/python/`, all served by the same page instance.
A ``Page`` using ``RoutablePageMixin`` exists within the page tree like any other page, but URL paths underneath it are checked against a list of patterns. If none of the patterns match, control is passed to subpages as usual (or failing that, a 404 error is thrown).
A `Page` using `RoutablePageMixin` exists within the page tree like any other page, but URL paths underneath it are checked against a list of patterns. If none of the patterns match, control is passed to subpages as usual (or failing that, a 404 error is thrown).
By default a route for ``r'^$'`` exists, which serves the content exactly like a normal ``Page`` would. It can be overridden by using ``@route(r'^$')`` on any other method of the inheriting class.
By default a route for `r'^$'` exists, which serves the content exactly like a normal `Page` would. It can be overridden by using `@route(r'^$')` on any other method of the inheriting class.
## Installation
Add `"wagtail.contrib.routable_page"` to your `INSTALLED_APPS`:
```python
INSTALLED_APPS = [
...
"wagtail.contrib.routable_page",
]
```
## The basics
To use `RoutablePageMixin`, you need to make your class inherit from both {class}`wagtail.contrib.routable_page.models.RoutablePageMixin` and {class}`wagtail.models.Page`, then define some view methods and decorate them with `wagtail.contrib.routable_page.models.route`. These view methods behave like ordinary Django view functions, and must return an `HttpResponse` object; typically this is done through a call to `django.shortcuts.render`.
Here's an example of an `EventIndexPage` with three views, assuming that an `EventPage` model with an `event_date` field has been defined elsewhere:
```python
import datetime
from django.http import JsonResponse
from wagtail.fields import RichTextField
from wagtail.models import Page
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
Installation
============
class EventIndexPage(RoutablePageMixin, Page):
Add ``"wagtail.contrib.routable_page"`` to your INSTALLED_APPS:
# Routable pages can have fields like any other - here we would
# render the intro text on a template with {{ page.intro|richtext }}
intro = RichTextField()
.. code-block:: python
INSTALLED_APPS = [
...
"wagtail.contrib.routable_page",
]
The basics
==========
To use ``RoutablePageMixin``, you need to make your class inherit from both :class:`wagtail.contrib.routable_page.models.RoutablePageMixin` and :class:`wagtail.models.Page`, then define some view methods and decorate them with ``wagtail.contrib.routable_page.models.route``. These view methods behave like ordinary Django view functions, and must return an ``HttpResponse`` object; typically this is done through a call to ``django.shortcuts.render``.
Here's an example of an ``EventIndexPage`` with three views, assuming that an ``EventPage`` model with an ``event_date`` field has been defined elsewhere:
.. code-block:: python
import datetime
from django.http import JsonResponse
from wagtail.fields import RichTextField
from wagtail.models import Page
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
class EventIndexPage(RoutablePageMixin, Page):
# Routable pages can have fields like any other - here we would
# render the intro text on a template with {{ page.intro|richtext }}
intro = RichTextField()
@route(r'^$') # will override the default Page serving mechanism
def current_events(self, request):
"""
View function for the current events page
"""
events = EventPage.objects.live().filter(event_date__gte=datetime.date.today())
# NOTE: We can use the RoutablePageMixin.render() method to render
# the page as normal, but with some of the context values overridden
return self.render(request, context_overrides={
'title': "Current events",
'events': events,
})
@route(r'^past/$')
def past_events(self, request):
"""
View function for the past events page
"""
events = EventPage.objects.live().filter(event_date__lt=datetime.date.today())
# NOTE: We are overriding the template here, as well as few context values
return self.render(
request,
context_overrides={
'title': "Past events",
'events': events,
},
template="events/event_index_historical.html",
)
# Multiple routes!
@route(r'^year/(\d+)/$')
@route(r'^year/current/$')
def events_for_year(self, request, year=None):
"""
View function for the events for year page
"""
if year is None:
year = datetime.date.today().year
events = EventPage.objects.live().filter(event_date__year=year)
return self.render(request, context_overrides={
'title': "Events for %d" % year,
'events': events,
})
@route(r'^year/(\d+)/count/$')
def count_for_year(self, request, year=None):
"""
View function that returns a simple JSON response that
includes the number of events scheduled for a specific year
"""
events = EventPage.objects.live().filter(event_date__year=year)
# NOTE: The usual template/context rendering process is irrelevant
# here, so we'll just return a HttpResponse directly
return JsonResponse({'count': events.count()})
Rendering other pages
=====================
Another way of returning an ``HttpResponse`` is to call the ``serve`` method of another page. (Calling a page's own ``serve`` method within a view method is not valid, as the view method is already being called within ``serve``, and this would create a circular definition).
For example, ``EventIndexPage`` could be extended with a ``next/`` route that displays the page for the next event:
.. code-block:: python
@route(r'^next/$')
def next_event(self, request):
@route(r'^$') # will override the default Page serving mechanism
def current_events(self, request):
"""
Display the page for the next event
View function for the current events page
"""
future_events = EventPage.objects.live().filter(event_date__gt=datetime.date.today())
next_event = future_events.order_by('event_date').first()
events = EventPage.objects.live().filter(event_date__gte=datetime.date.today())
return next_event.serve(request)
# NOTE: We can use the RoutablePageMixin.render() method to render
# the page as normal, but with some of the context values overridden
return self.render(request, context_overrides={
'title': "Current events",
'events': events,
})
@route(r'^past/$')
def past_events(self, request):
"""
View function for the past events page
"""
events = EventPage.objects.live().filter(event_date__lt=datetime.date.today())
Reversing URLs
==============
# NOTE: We are overriding the template here, as well as few context values
return self.render(
request,
context_overrides={
'title': "Past events",
'events': events,
},
template="events/event_index_historical.html",
)
:class:`~models.RoutablePageMixin` adds a :meth:`~models.RoutablePageMixin.reverse_subpage` method to your page model which you can use for reversing URLs. For example:
# Multiple routes!
@route(r'^year/(\d+)/$')
@route(r'^year/current/$')
def events_for_year(self, request, year=None):
"""
View function for the events for year page
"""
if year is None:
year = datetime.date.today().year
.. code-block:: python
events = EventPage.objects.live().filter(event_date__year=year)
return self.render(request, context_overrides={
'title': "Events for %d" % year,
'events': events,
})
@route(r'^year/(\d+)/count/$')
def count_for_year(self, request, year=None):
"""
View function that returns a simple JSON response that
includes the number of events scheduled for a specific year
"""
events = EventPage.objects.live().filter(event_date__year=year)
# NOTE: The usual template/context rendering process is irrelevant
# here, so we'll just return a HttpResponse directly
return JsonResponse({'count': events.count()})
```
### Rendering other pages
Another way of returning an `HttpResponse` is to call the `serve` method of another page. (Calling a page's own `serve` method within a view method is not valid, as the view method is already being called within `serve`, and this would create a circular definition).
For example, `EventIndexPage` could be extended with a `next/` route that displays the page for the next event:
```python
@route(r'^next/$')
def next_event(self, request):
"""
Display the page for the next event
"""
future_events = EventPage.objects.live().filter(event_date__gt=datetime.date.today())
next_event = future_events.order_by('event_date').first()
return next_event.serve(request)
```
### Reversing URLs
{class}`~models.RoutablePageMixin` adds a {meth}`~models.RoutablePageMixin.reverse_subpage` method to your page model which you can use for reversing URLs. For example:
```python
# The URL name defaults to the view method name.
>>> event_page.reverse_subpage('events_for_year', args=(2015, ))
'year/2015/'
```
This method only returns the part of the URL within the page. To get the full URL, you must append it to the values of either the :attr:`~wagtail.models.Page.url` or the :attr:`~wagtail.models.Page.full_url` attribute on your page:
This method only returns the part of the URL within the page. To get the full URL, you must append it to the values of either the {attr}`~wagtail.models.Page.url` or the {attr}`~wagtail.models.Page.full_url` attribute on your page:
.. code-block:: python
```python
>>> event_page.url + event_page.reverse_subpage('events_for_year', args=(2015, ))
'/events/year/2015/'
>>> event_page.url + event_page.reverse_subpage('events_for_year', args=(2015, ))
'/events/year/2015/'
>>> event_page.full_url + event_page.reverse_subpage('events_for_year', args=(2015, ))
'http://example.com/events/year/2015/'
```
>>> event_page.full_url + event_page.reverse_subpage('events_for_year', args=(2015, ))
'http://example.com/events/year/2015/'
### Changing route names
Changing route names
--------------------
The route name defaults to the name of the view. You can override this name with the `name` keyword argument on `@route`:
The route name defaults to the name of the view. You can override this name with the ``name`` keyword argument on ``@route``:
.. code-block:: python
from wagtail.models import Page
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
```python
from wagtail.models import Page
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
class EventPage(RoutablePageMixin, Page):
class EventPage(RoutablePageMixin, Page):
...
@route(r'^year/(\d+)/$', name='year')
def events_for_year(self, request, year):
"""
View function for the events for year page
"""
...
```
@route(r'^year/(\d+)/$', name='year')
def events_for_year(self, request, year):
"""
View function for the events for year page
"""
...
```python
>>> event_page.url + event_page.reverse_subpage('year', args=(2015, ))
'/events/year/2015/'
```
.. code-block:: python
>>> event_page.url + event_page.reverse_subpage('year', args=(2015, ))
'/events/year/2015/'
The ``RoutablePageMixin`` class
===============================
## The `RoutablePageMixin` class
```{eval-rst}
.. automodule:: wagtail.contrib.routable_page.models
.. autoclass:: RoutablePageMixin
@ -198,27 +189,30 @@ The ``RoutablePageMixin`` class
.. automethod:: reverse_subpage
Example:
```
.. code-block:: python
Example:
url = page.url + page.reverse_subpage('events_for_year', kwargs={'year': '2014'})
```python
url = page.url + page.reverse_subpage('events_for_year', kwargs={'year': '2014'})
```
(routablepageurl_template_tag)=
.. _routablepageurl_template_tag:
The ``routablepageurl`` template tag
====================================
## The `routablepageurl` template tag
```{eval-rst}
.. currentmodule:: wagtail.contrib.routable_page.templatetags.wagtailroutablepage_tags
.. autofunction:: routablepageurl
Example:
```
.. code-block:: html+django
Example:
{% load wagtailroutablepage_tags %}
```html+django
{% load wagtailroutablepage_tags %}
{% routablepageurl page "feed" %}
{% routablepageurl page "archive" 2014 08 14 %}
{% routablepageurl page "food" foo="bar" baz="quux" %}
{% routablepageurl page "feed" %}
{% routablepageurl page "archive" 2014 08 14 %}
{% routablepageurl page "food" foo="bar" baz="quux" %}
```

Wyświetl plik

@ -1,5 +1,3 @@
(site_settings)=
# Settings
The `wagtail.contrib.settings` module allows you to define models that hold
@ -148,6 +146,8 @@ def view(request):
...
```
(site_settings)=
#### Site-specific settings
If you require access to a site-specific setting in a view, the

Wyświetl plik

@ -1,47 +1,43 @@
.. _simple_translation:
(simple_translation)=
Simple translation
==================
# Simple translation
The simple_translation module provides a user interface that allows users to copy pages and translatable snippets into another language.
- Copies are created in the source language (not translated)
- Copies of pages are in draft status
- Copies are created in the source language (not translated)
- Copies of pages are in draft status
Content editors need to translate the content and publish the pages.
.. note::
Simple Translation is optional. It can be switched out by third-party packages. Like the more advanced `wagtail-localize <https://github.com/wagtail/wagtail-localize>`_.
```{note}
Simple Translation is optional. It can be switched out by third-party packages. Like the more advanced [`wagtail-localize`](https://github.com/wagtail/wagtail-localize).
```
## Basic configuration
Basic configuration
~~~~~~~~~~~~~~~~~~~
Add `"wagtail.contrib.simple_translation"` to `INSTALLED_APPS` in your settings file:
Add ``"wagtail.contrib.simple_translation"`` to INSTALLED_APPS in your settings file:
```python
INSTALLED_APPS = [
...
"wagtail.contrib.simple_translation",
]
```
.. code-block:: python
INSTALLED_APPS = [
...
"wagtail.contrib.simple_translation",
]
Run ``python manage.py migrate`` to create the necessary permissions.
Run `python manage.py migrate` to create the necessary permissions.
In the Wagtail admin, go to settings and give some users or groups the "Can submit translations" permission.
Page tree synchronisation
~~~~~~~~~~~~~~~~~~~~~~~~~
## Page tree synchronisation
Depending on your use case, it may be useful to keep the page trees in sync between different locales.
You can enable this feature by setting `WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE` to `True`.
.. code-block:: python
WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE = True
```python
WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE = True
```
When this feature is turned on, every time an editor creates a page, Wagtail creates an alias for that page under the page trees of all the other locales.
For example, when an editor creates the page "/en/blog/my-blog-post/", Wagtail creates an alias of that page at "/fr/blog/my-blog-post/" and "/de/blog/my-blog-post/".
For example, when an editor creates the page `"/en/blog/my-blog-post/"`, Wagtail creates an alias of that page at `"/fr/blog/my-blog-post/"` and `"/de/blog/my-blog-post/"`.

Wyświetl plik

@ -1,124 +1,108 @@
.. _sitemap_generation:
(sitemap_generation)=
Sitemap generator
=================
# Sitemap generator
This document describes how to create XML sitemaps for your Wagtail website
using the ``wagtail.contrib.sitemaps`` module.
using the `wagtail.contrib.sitemaps` module.
```{note}
As of Wagtail 1.10 the Django contrib sitemap app is used to generate
sitemaps. However since Wagtail requires the Site instance to be available
during the sitemap generation you will have to use the views from the
`wagtail.contrib.sitemaps.views` module instead of the views
provided by Django (`django.contrib.sitemaps.views`).
.. note::
The usage of these views is otherwise identical, which means that
customisation and caching of the sitemaps are done using the default Django
patterns. See the Django documentation for in-depth information.
```
As of Wagtail 1.10 the Django contrib sitemap app is used to generate
sitemaps. However since Wagtail requires the Site instance to be available
during the sitemap generation you will have to use the views from the
``wagtail.contrib.sitemaps.views`` module instead of the views
provided by Django (``django.contrib.sitemaps.views``).
## Basic configuration
The usage of these views is otherwise identical, which means that
customisation and caching of the sitemaps are done using the default Django
patterns. See the Django documentation for in-depth information.
Basic configuration
~~~~~~~~~~~~~~~~~~~
You firstly need to add ``"django.contrib.sitemaps"`` to INSTALLED_APPS in your
You firstly need to add `"django.contrib.sitemaps"` to INSTALLED_APPS in your
Django settings file:
.. code-block:: python
```python
INSTALLED_APPS = [
...
INSTALLED_APPS = [
...
"django.contrib.sitemaps",
]
```
"django.contrib.sitemaps",
]
Then, in ``urls.py``, you need to add a link to the
``wagtail.contrib.sitemaps.views.sitemap`` view which generates the
Then, in `urls.py`, you need to add a link to the
`wagtail.contrib.sitemaps.views.sitemap` view which generates the
sitemap:
.. code-block:: python
```python
from wagtail.contrib.sitemaps.views import sitemap
from wagtail.contrib.sitemaps.views import sitemap
urlpatterns = [
...
urlpatterns = [
...
path('sitemap.xml', sitemap),
path('sitemap.xml', sitemap),
...
...
# Ensure that the 'sitemap' line appears above the default Wagtail page serving route
re_path(r'', include(wagtail_urls)),
]
```
# Ensure that the 'sitemap' line appears above the default Wagtail page serving route
re_path(r'', include(wagtail_urls)),
]
You should now be able to browse to ``/sitemap.xml`` and see the sitemap
You should now be able to browse to `/sitemap.xml` and see the sitemap
working. By default, all published pages in your website will be added to the
site map.
Setting the hostname
~~~~~~~~~~~~~~~~~~~~
## Setting the hostname
By default, the sitemap uses the hostname defined in the Wagtail Admin's
``Sites`` area. If your default site is called ``localhost``, then URLs in the
`Sites` area. If your default site is called `localhost`, then URLs in the
sitemap will look like:
.. code-block:: xml
<url>
<loc>http://localhost/about/</loc>
<lastmod>2015-09-26</lastmod>
</url>
```xml
<url>
<loc>http://localhost/about/</loc>
<lastmod>2015-09-26</lastmod>
</url>
```
For tools like Google Search Tools to properly index your site, you need to set
a valid, crawlable hostname. If you change the site's hostname from
``localhost`` to ``mysite.com``, ``sitemap.xml`` will contain the correct URLs:
`localhost` to `mysite.com`, `sitemap.xml` will contain the correct URLs:
.. code-block:: xml
```xml
<url>
<loc>http://mysite.com/about/</loc>
<lastmod>2015-09-26</lastmod>
</url>
```
<url>
<loc>http://mysite.com/about/</loc>
<lastmod>2015-09-26</lastmod>
</url>
If you change the site's port to `443`, the `https` scheme will be used.
Find out more about [working with Sites](site_model_ref).
## Customising
If you change the site's port to ``443``, the ``https`` scheme will be used.
Find out more about :ref:`working with Sites<site_model_ref>`.
### URLs
Customising
~~~~~~~~~~~
URLs
----
The ``Page`` class defines a ``get_sitemap_urls`` method which you can
override to customise sitemaps per ``Page`` instance. This method must accept
The `Page` class defines a `get_sitemap_urls` method which you can
override to customise sitemaps per `Page` instance. This method must accept
a request object and return a list of dictionaries, one dictionary per URL
entry in the sitemap. You can exclude pages from the sitemap by returning an
empty list.
Each dictionary can contain the following:
- **location** (required) - This is the full URL path to add into the sitemap.
- **lastmod** - A python date or datetime set to when the page was last modified.
- **changefreq**
- **priority**
- **location** (required) - This is the full URL path to add into the sitemap.
- **lastmod** - A python date or datetime set to when the page was last modified.
- **changefreq**
- **priority**
You can add more but you will need to override the
``sitemap.xml`` template in order for them to be displayed in the sitemap.
`sitemap.xml` template in order for them to be displayed in the sitemap.
Serving multiple sitemaps
~~~~~~~~~~~~~~~~~~~~~~~~~
## Serving multiple sitemaps
If you want to support the sitemap indexes from Django then you will need to
use the index view from ``wagtail.contrib.sitemaps.views`` instead of the index
view from ``django.contrib.sitemaps.views``. Please see the Django
use the index view from `wagtail.contrib.sitemaps.views` instead of the index
view from `django.contrib.sitemaps.views`. Please see the Django
documentation for further details.

Wyświetl plik

@ -1,181 +1,165 @@
# TableBlock
TableBlock
==========
The TableBlock module provides an HTML table block type for StreamField. This module uses [handsontable 6.2.2](https://handsontable.com/) to provide users with the ability to create and edit HTML tables in Wagtail. Table blocks provides a caption field for accessibility.
The TableBlock module provides an HTML table block type for StreamField. This module uses `handsontable 6.2.2 <https://handsontable.com/>`_ to provide users with the ability to create and edit HTML tables in Wagtail. Table blocks provides a caption field for accessibility.
![](../../_static/images/screen40_table_block.png)
.. image:: ../../_static/images/screen40_table_block.png
## Installation
Add `"wagtail.contrib.table_block"` to your INSTALLED_APPS:
Installation
------------
```python
INSTALLED_APPS = [
...
Add ``"wagtail.contrib.table_block"`` to your INSTALLED_APPS:
"wagtail.contrib.table_block",
]
```
.. code-block:: python
INSTALLED_APPS = [
...
"wagtail.contrib.table_block",
]
Basic Usage
-----------
## Basic Usage
After installation, the TableBlock module can be used in a similar fashion to other StreamField blocks in the Wagtail core.
Import the TableBlock ``from wagtail.contrib.table_block.blocks import TableBlock`` and add it to your StreamField declaration.
Import the TableBlock `from wagtail.contrib.table_block.blocks import TableBlock` and add it to your StreamField declaration.
.. code-block:: python
```python
class DemoStreamBlock(StreamBlock):
...
table = TableBlock()
```
class DemoStreamBlock(StreamBlock):
...
table = TableBlock()
Then, on your page template, the `{% include_block %}` tag (called on either the individual block, or the StreamField value as a whole) will render any table blocks it encounters as an HTML `<table>` element:
```html+django
{% load wagtailcore_tags %}
Then, on your page template, the ``{% include_block %}`` tag (called on either the individual block, or the StreamField value as a whole) will render any table blocks it encounters as an HTML ``<table>`` element:
.. code-block:: html+django
{% load wagtailcore_tags %}
{% include_block page.body %}
{% include_block page.body %}
```
Or:
.. code-block:: html+django
```html+django
{% load wagtailcore_tags %}
{% load wagtailcore_tags %}
{% for block in page.body %}
{% if block.block_type == 'table' %}
{% include_block block %}
{% else %}
{# rendering for other block types #}
{% endif %}
{% endfor %}
```
{% for block in page.body %}
{% if block.block_type == 'table' %}
{% include_block block %}
{% else %}
{# rendering for other block types #}
{% endif %}
{% endfor %}
## Advanced Usage
### Default Configuration
Advanced Usage
--------------
When defining a TableBlock, Wagtail provides the ability to pass an optional `table_options` dictionary. The default TableBlock dictionary looks like this:
Default Configuration
^^^^^^^^^^^^^^^^^^^^^
```python
default_table_options = {
'minSpareRows': 0,
'startRows': 3,
'startCols': 3,
'colHeaders': False,
'rowHeaders': False,
'contextMenu': [
'row_above',
'row_below',
'---------',
'col_left',
'col_right',
'---------',
'remove_row',
'remove_col',
'---------',
'undo',
'redo'
],
'editor': 'text',
'stretchH': 'all',
'height': 108,
'language': language,
'renderer': 'text',
'autoColumnSize': False,
}
```
When defining a TableBlock, Wagtail provides the ability to pass an optional ``table_options`` dictionary. The default TableBlock dictionary looks like this:
### Configuration Options
.. code-block:: python
Every key in the `table_options` dictionary maps to a [handsontable](https://handsontable.com/) option. These settings can be changed to alter the behaviour of tables in Wagtail. The following options are available:
default_table_options = {
'minSpareRows': 0,
'startRows': 3,
'startCols': 3,
'colHeaders': False,
'rowHeaders': False,
'contextMenu': [
'row_above',
'row_below',
'---------',
'col_left',
'col_right',
'---------',
'remove_row',
'remove_col',
'---------',
'undo',
'redo'
],
'editor': 'text',
'stretchH': 'all',
'height': 108,
'language': language,
'renderer': 'text',
'autoColumnSize': False,
}
- [minSpareRows](https://handsontable.com/docs/6.2.2/Options.html#minSpareRows) - The number of rows to append to the end of an empty grid. The default setting is 0.
- [startRows](https://handsontable.com/docs/6.2.2/Options.html#startRows) - The default number of rows for a new table.
- [startCols](https://handsontable.com/docs/6.2.2/Options.html#startCols) - The default number of columns for new tables.
- [colHeaders](https://handsontable.com/docs/6.2.2/Options.html#colHeaders) - Can be set to `True` or `False`. This setting designates if new tables should be created with column headers. **Note:** this only sets the behaviour for newly created tables. Page editors can override this by checking the the “Column header” checkbox in the table editor in the Wagtail admin.
- [rowHeaders](https://handsontable.com/docs/6.2.2/Options.html#rowHeaders) - Operates the same as `colHeaders` to designate if new tables should be created with the first column as a row header. Just like `colHeaders` this option can be overridden by the page editor in the Wagtail admin.
- [contextMenu](https://handsontable.com/docs/6.2.2/Options.html#contextMenu) - Enables or disables the Handsontable right-click menu. By default this is set to `True`. Alternatively you can provide a list or a dictionary with [specific options](https://handsontable.com/docs/6.2.2/demo-context-menu.html#page-specific).
- [editor](https://handsontable.com/docs/6.2.2/Options.html#editor) - Defines the editor used for table cells. The default setting is text.
- [stretchH](https://handsontable.com/docs/6.2.2/Options.html#stretchH) - Sets the default horizontal resizing of tables. Options include, 'none', 'last', and 'all'. By default TableBlock uses 'all' for the even resizing of columns.
- [height](https://handsontable.com/docs/6.2.2/Options.html#height) - The default height of the grid. By default TableBlock sets the height to `108` for the optimal appearance of new tables in the editor. This is optimized for tables with `startRows` set to `3`. If you change the number of `startRows` in the configuration, you might need to change the `height` setting to improve the default appearance in the editor.
- [language](https://handsontable.com/docs/6.2.2/Options.html#language) - The default language setting. By default TableBlock tries to get the language from `django.utils.translation.get_language`. If needed, this setting can be overridden here.
- [renderer](https://handsontable.com/docs/6.2.2/Options.html#renderer) - The default setting Handsontable uses to render the content of table cells.
- [autoColumnSize](https://handsontable.com/docs/6.2.2/Options.html#autoColumnSize) - Enables or disables the `autoColumnSize` plugin. The TableBlock default setting is `False`.
A [complete list of handsontable options](https://handsontable.com/docs/6.2.2/Options.html) can be found on the Handsontable website.
Configuration Options
^^^^^^^^^^^^^^^^^^^^^
Every key in the ``table_options`` dictionary maps to a `handsontable <https://handsontable.com/>`_ option. These settings can be changed to alter the behaviour of tables in Wagtail. The following options are available:
* `minSpareRows <https://handsontable.com/docs/6.2.2/Options.html#minSpareRows>`_ - The number of rows to append to the end of an empty grid. The default setting is 0.
* `startRows <https://handsontable.com/docs/6.2.2/Options.html#startRows>`_ - The default number of rows for a new table.
* `startCols <https://handsontable.com/docs/6.2.2/Options.html#startCols>`_ - The default number of columns for new tables.
* `colHeaders <https://handsontable.com/docs/6.2.2/Options.html#colHeaders>`_ - Can be set to ``True`` or ``False``. This setting designates if new tables should be created with column headers. **Note:** this only sets the behaviour for newly created tables. Page editors can override this by checking the the “Column header” checkbox in the table editor in the Wagtail admin.
* `rowHeaders <https://handsontable.com/docs/6.2.2/Options.html#rowHeaders>`_ - Operates the same as ``colHeaders`` to designate if new tables should be created with the first column as a row header. Just like ``colHeaders`` this option can be overridden by the page editor in the Wagtail admin.
* `contextMenu <https://handsontable.com/docs/6.2.2/Options.html#contextMenu>`_ - Enables or disables the Handsontable right-click menu. By default this is set to ``True``. Alternatively you can provide a list or a dictionary with [specific options](https://handsontable.com/docs/6.2.2/demo-context-menu.html#page-specific).
* `editor <https://handsontable.com/docs/6.2.2/Options.html#editor>`_ - Defines the editor used for table cells. The default setting is text.
* `stretchH <https://handsontable.com/docs/6.2.2/Options.html#stretchH>`_ - Sets the default horizontal resizing of tables. Options include, 'none', 'last', and 'all'. By default TableBlock uses 'all' for the even resizing of columns.
* `height <https://handsontable.com/docs/6.2.2/Options.html#height>`_ - The default height of the grid. By default TableBlock sets the height to ``108`` for the optimal appearance of new tables in the editor. This is optimized for tables with ``startRows`` set to ``3``. If you change the number of ``startRows`` in the configuration, you might need to change the ``height`` setting to improve the default appearance in the editor.
* `language <https://handsontable.com/docs/6.2.2/Options.html#language>`_ - The default language setting. By default TableBlock tries to get the language from ``django.utils.translation.get_language``. If needed, this setting can be overridden here.
* `renderer <https://handsontable.com/docs/6.2.2/Options.html#renderer>`_ - The default setting Handsontable uses to render the content of table cells.
* `autoColumnSize <https://handsontable.com/docs/6.2.2/Options.html#autoColumnSize>`_ - Enables or disables the ``autoColumnSize`` plugin. The TableBlock default setting is ``False``.
A `complete list of handsontable options <https://handsontable.com/docs/6.2.2/Options.html>`_ can be found on the Handsontable website.
Changing the default table_options
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
### Changing the default table_options
To change the default table options just pass a new table_options dictionary when a new TableBlock is declared.
.. code-block:: python
```python
new_table_options = {
'minSpareRows': 0,
'startRows': 6,
'startCols': 4,
'colHeaders': False,
'rowHeaders': False,
'contextMenu': True,
'editor': 'text',
'stretchH': 'all',
'height': 216,
'language': 'en',
'renderer': 'text',
'autoColumnSize': False,
}
new_table_options = {
'minSpareRows': 0,
'startRows': 6,
'startCols': 4,
'colHeaders': False,
'rowHeaders': False,
'contextMenu': True,
'editor': 'text',
'stretchH': 'all',
'height': 216,
'language': 'en',
'renderer': 'text',
'autoColumnSize': False,
}
class DemoStreamBlock(StreamBlock):
...
table = TableBlock(table_options=new_table_options)
```
class DemoStreamBlock(StreamBlock):
...
table = TableBlock(table_options=new_table_options)
Supporting cell alignment
^^^^^^^^^^^^^^^^^^^^^^^^^^
### Supporting cell alignment
You can activate the `alignment` option by setting a custom `contextMenu` which allows you to set the alignment on a cell selection.
HTML classes set by handsontable will be kept on the rendered block. You'll then be able to apply your own custom CSS rules to preserve the style. Those class names are:
* Horizontal: ``htLeft``, ``htCenter``, ``htRight``, ``htJustify``
* Vertical: ``htTop``, ``htMiddle``, ``htBottom``
- Horizontal: `htLeft`, `htCenter`, `htRight`, `htJustify`
- Vertical: `htTop`, `htMiddle`, `htBottom`
.. code-block:: python
```python
new_table_options = {
'contextMenu': [
'row_above',
'row_below',
'---------',
'col_left',
'col_right',
'---------',
'remove_row',
'remove_col',
'---------',
'undo',
'redo',
'---------',
'copy',
'cut'
'---------',
'alignment',
],
}
new_table_options = {
'contextMenu': [
'row_above',
'row_below',
'---------',
'col_left',
'col_right',
'---------',
'remove_row',
'remove_col',
'---------',
'undo',
'redo',
'---------',
'copy',
'cut'
'---------',
'alignment',
],
}
class DemoStreamBlock(StreamBlock):
...
table = TableBlock(table_options=new_table_options)
class DemoStreamBlock(StreamBlock):
...
table = TableBlock(table_options=new_table_options)
```

Wyświetl plik

@ -4,7 +4,7 @@ The `typed_table_block` module provides a StreamField block type for building ta
## Installation
Add `"wagtail.contrib.typed_table_block"` to your INSTALLED_APPS:
Add `"wagtail.contrib.typed_table_block"` to your `INSTALLED_APPS`:
```python
INSTALLED_APPS = [
@ -36,20 +36,20 @@ class DemoStreamBlock(blocks.StreamBlock):
To keep the UI as simple as possible for authors, it's generally recommended to use Wagtail's basic built-in block types as column types, as above. However, all custom block types and parameters are supported. For example, to define a 'country' column type consisting of a dropdown of country choices:
```python
table = TypedTableBlock([
('text', blocks.CharBlock()),
('numeric', blocks.FloatBlock()),
('rich_text', blocks.RichTextBlock()),
('image', ImageChooserBlock()),
('country', ChoiceBlock(choices=[
('be', 'Belgium'),
('fr', 'France'),
('de', 'Germany'),
('nl', 'Netherlands'),
('pl', 'Poland'),
('uk', 'United Kingdom'),
])),
])
table = TypedTableBlock([
('text', blocks.CharBlock()),
('numeric', blocks.FloatBlock()),
('rich_text', blocks.RichTextBlock()),
('image', ImageChooserBlock()),
('country', ChoiceBlock(choices=[
('be', 'Belgium'),
('fr', 'France'),
('de', 'Germany'),
('nl', 'Netherlands'),
('pl', 'Poland'),
('uk', 'United Kingdom'),
])),
])
```
On your page template, the `{% include_block %}` tag (called on either the individual block, or the StreamField value as a whole) will render any typed table blocks as an HTML `<table>` element.