# 📝 Use Pages
# 📎 The Page Model
Camomilla has it's own page model. The page model has an attribute for every relevant data in a web page. It takes care of SEO data and permalinks and template, and exposes jsonfields to manage additional data.
To use camomilla pages you need to add the dynamic url resolver at the end of your website urlpatterns:
# <project_name>/urls.py
urlpatterns += path('', include("camomilla.dynamic_pages_urls"))
To handle multilanguage pages use:
# <project_name>/urls.py
urlpatterns += i18n_patterns(
path('', include("camomilla.dynamic_pages_urls")),
prefix_default_language=False
)
This resolver is made to check any permalink that does not match anything in the url pattern and look in the database for a page with that permalink.
⚠️ Beware!
The dynamic_pages_urls
handler should always be the last handler of your urlpatterns list.
# Choose the template
The page model has a field that determines which html template to use for rendering.
The default value is defaults/pages/default.html
. If a value is stored in this field it will be used. The rendering part is yelded by the default django template engine (opens new window).
The default value can be changed with a setting:
# <project_name>/settings.py
CAMOMILLA = {
"RENDER": { "PAGE": {"DEFAULT_TEMPLATE": "website/my_template.html" }}
}
# Add template data
⚠️ Beware!
🧩 Use Page Context to inject context in a more safe way.
The data that will be available in the rendering engine should be saved in the template_data field. This field is a django JSONField. So it will accept any json structure.
The data in this field will be accessible inside the template through page.template_data
.
If you need to enrich the template context with other data that are not stored in the template data you can provide an inject_context
function in camomilla settings:
# <project_name>/settings.py
# import from external file
from my_project_app.template_rendering import page_inject_context
# or define explicitly in settings
def page_inject_context(request, super_ctx):
from my_project_app.models import Category
return {"all_categories": Category.objects.all()} # the all_category values will be accessible inside the template context.
CAMOMILLA = {
"RENDER": { "PAGE": {"INJECT_CONTEXT": page_inject_context }}
}
# Set the permalink
By default camomilla Page gives you the possibility to define the page slug
field only.
The permalink will be generated from page slug
and parent_page
field.
With parent page field you can set a parent-child structure to pages.
For example to generate the permalink slug1/slug2
you will need to create 2 Pages, one with slug slug1
and one with the slug slug2
then set the first page as the parent page of the second.
# 🖇️ The AbstractPage Model
Camomilla comes with an AbstractPage
model. AbstractPage is different from Page model, it is Abstract.
This means that the AbstractPage by itself does not create a database table. The only whay an AbstractPage can have its own db table is to be inherited by a concrete model like this:
from camomilla.models import AbstractPage
class MyPageModel(AbstractPage):
# ... custom logic there
pass
The AbstractPage contains all the logics of Page model. This means that you can override anything and define multiple custom page models. This is very usefull if you need to build complex sites, where you can have also other kid of data that need to be rendered and treated like a page (es. product, category, blog, anything..).
You can also define some intresting properties when you are defining your own page model.:
# Define page options with PageMeta
Many "settings" of the page model are stored inside a PageMeta
class like this:
from camomilla.models import AbstractPage
class MyPageModel(AbstractPage):
class PageMeta:
parent_page_field = "parent_page"
default_template = "website/my_template.html"
def inject_context_func(request, super_ctx):
from my_project_app.models import Category
return {"all_categories": Category.objects.all()}
From PageMeta class you can define:
parent_page_field
==> set a different propery to store page parent. Likecategory
or anything else.default_template
==> set a different default template.inject_context_func
==> add more data inside template context
⚠️ Beware!
🧩 Use Page Context to inject context in a more safe way.
# Override the page context
This has almost the same functionality of inject_context_func
PageMeta option but it runs at a more deep level. Overriding this will override the context and not only add things to it.
from camomilla.models import AbstractPage
class MyPageModel(AbstractPage):
def get_context(request):
from my_project_app.models import Category
return {"page": self, "all_categories": Category.objects.all()}
We suggest to always return { "page": self }
in the context.
# 🌐 Build a sitemap.xml
To build a sitemap.xml you can use the standard django sitemap framework. Camomilla comes with a CamomillaPageSitemap
class that you can use to include camomilla pages in your sitemap.xml.
# <project_name>/sitemap.py
from django.contrib.sitemaps import Sitemap
from camomilla.sitemaps import CamomillaPageSitemap
# declare your custom sitemaps
class StaticViewSitemap(Sitemap):
def items(self):
return ['home', 'about', 'contact']
def location(self, item):
return reverse(item)
sitemaps = {
'pages': CamomillaPageSitemap, # add the camomilla sitemap
'static': StaticViewSitemap, # add your custom sitemaps to the camomilla sitemaps
}
# <project_name>/urls.py
from django.contrib.sitemaps.views import sitemap
from .sitemap import sitemaps
urlpatterns += [
path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap')
]
To customize the camomilla sitemap you can override the CamomillaPageSitemap
class. Overriding this class will allow you to customize changefreq, priority and also items.
from camomilla.sitemaps import CamomillaPageSitemap
class MyCamomillaPageSitemap(CamomillaPageSitemap):
changefreq = "monthly"
priority = 0.5
def items(self):
return super().items().filter(status="PUB")
Remember that items is a queryset of UrlNode
objects and to access the page model you need to use the page
property of the UrlNode
object.
# 🗂️ Pages router API endpoint
⚠️ Beware!
If you need to create an api endpoint for a model inheriting from the AbstractPage
model remember to use inside the serializer the AbstractPageMixin
.
Camomilla comes with a builtin pages router endpoint that allows you to browse your pages by url.
URL Structure:
api/camomilla/pages-router/<page_url>
You can provide as the page_url parameter the full url of the page without language prefix. The language you want to match against is taken from the request params. If no language is specified, the default language is used.
To specify the language in the request params you can use the lang
parameter:
/api/camomilla/pages-router/<page_url>?lang=en
When you try to get a page from the url, the router will always take in to account the active language. If you are trying to get a page with a certain language, make sure to set the language in the request params. If the specified url is not present in current language, the router endpoint will return a 404 error.
Simple Response:
{
"id": 1,
"is_public": false,
"status": "DRF",
"indexable": true,
"alternates": {
"it": "/",
"en": "/en/"
},
"permalink": "/",
"related_name": "camomilla_page",
"breadcrumbs": [
{
"permalink": "/",
"title": null
}
],
"routerlink": "/",
"template": "website/home.html",
"title": null,
"description": null,
"og_description": null,
"og_title": null,
"og_type": null,
"og_url": null,
"canonical": null,
"meta": {},
"date_created": "2023-09-07T13:16:24.762269Z",
"date_updated_at": "2023-09-08T17:38:03.155480Z",
"breadcrumbs_title": null,
"slug": null,
"template_data": {},
"identifier": "4ee68c88-dd1a-4515-bf50-7839f2cf1e72",
"pubblication_date": null,
"ordering": 0,
"og_image": null,
"url_node": {
"id": 46,
"permalink": "/",
"related_name": "camomilla_page"
},
"parent_page": null
}
The respose will not include field translations since the response has the content already translated in the active language.
But if you need also some other language you can specify the included_translations
parameter in the request.
/api/camomilla/pages-router/<page_url>?included_translations=it
The included_translations
parameter accepts a comma separated list of languages or the value all
to include all the translations.