|
@@ -0,0 +1,384 @@
|
|
|
+Your first Wagtail site
|
|
|
+=======================
|
|
|
+
|
|
|
+1. Install Wagtail and its dependencies:
|
|
|
+
|
|
|
+ ``pip install wagtail``
|
|
|
+
|
|
|
+2. Start your site:
|
|
|
+
|
|
|
+ ``wagtail start mysite``
|
|
|
+
|
|
|
+ Wagtail provides a ``start`` command similar to
|
|
|
+ ``django-admin.py startproject``. Running ``wagtail start mysite`` in
|
|
|
+ your project will generate a new ``mysite`` folder with a few
|
|
|
+ Wagtail-specific extras, including the required project settings, a
|
|
|
+ ``requirements.txt`` file to track your project's dependencies, and a
|
|
|
+ "core" app with a blank ``HomePage`` model and basic templates
|
|
|
+
|
|
|
+3. Create the database:
|
|
|
+
|
|
|
+ ``python manage.py migrate``
|
|
|
+
|
|
|
+ If you haven't updated the project settings, this will be a SQLite
|
|
|
+ database file in the project directory.
|
|
|
+
|
|
|
+4. Create an admin user:
|
|
|
+
|
|
|
+ ``python manage.py createsuperuser``.
|
|
|
+
|
|
|
+5. ``python manage.py runserver`` If everything worked,
|
|
|
+ http://127.0.0.1:8000 will show you a welcome page
|
|
|
+
|
|
|
+ .. figure:: ../_static/images/tutorial/tutorial_1.png
|
|
|
+ :alt: Wagtail welcome message
|
|
|
+
|
|
|
+ You can now access the administrative area at ``/admin``
|
|
|
+
|
|
|
+ .. figure:: ../_static/images/tutorial/tutorial_2.png
|
|
|
+ :alt: Administrative screen
|
|
|
+
|
|
|
+Extend the HomePage model
|
|
|
+-------------------------
|
|
|
+
|
|
|
+Out of the box you get a blank HomePage model and the core app has a
|
|
|
+migration that creates a homepage and configures Wagtail to use it.
|
|
|
+
|
|
|
+This example extends the HomePage model to add a body field.
|
|
|
+
|
|
|
+.. code:: python
|
|
|
+
|
|
|
+ from django.db import models
|
|
|
+
|
|
|
+ from wagtail.wagtailcore.models import Page
|
|
|
+ from wagtail.wagtailcore.fields import RichTextField
|
|
|
+ from wagtail.wagtailadmin.edit_handlers import FieldPanel
|
|
|
+
|
|
|
+
|
|
|
+ class HomePage(Page):
|
|
|
+ body = RichTextField(blank=True)
|
|
|
+
|
|
|
+ content_panels = Page.content_panels + [
|
|
|
+ FieldPanel('title', classname="full title"),
|
|
|
+ FieldPanel('intro')
|
|
|
+ ]
|
|
|
+
|
|
|
+``body`` is defined as ``RichTextField``, a special Wagtail field. You
|
|
|
+can use any of the Django core fields. ``content_panels`` define the
|
|
|
+capabilities and the layout of the editing interface. :doc:`More on creating Page models. <../topics/creating_pages>`
|
|
|
+
|
|
|
+
|
|
|
+Run ``python manage.py makemigrations``, then
|
|
|
+``python manage.py migrate`` to update the database with your model
|
|
|
+changes. You must run the above commands each time you make changes to
|
|
|
+the model definition.
|
|
|
+
|
|
|
+To reflect the model changes, edit
|
|
|
+``core/templates/core/home_page.html``. Wagtail uses normal Django
|
|
|
+templates to render each page type. It automatically generates a
|
|
|
+suggestion by separating capital letters with underscores (e.g. HomePage
|
|
|
+becomes home\_page.html).
|
|
|
+
|
|
|
+.. code:: html+django
|
|
|
+
|
|
|
+ {% extends "base.html" %}
|
|
|
+
|
|
|
+ {% load static core_tags wagtailcore_tags %}
|
|
|
+
|
|
|
+ {% block body_class %}template-{{ self.get_verbose_name|slugify }}{% endblock %}
|
|
|
+
|
|
|
+ {% block content %}
|
|
|
+ {{ self.body|richtext }}
|
|
|
+ {% endblock %}
|
|
|
+
|
|
|
+.. figure:: ../_static/images/tutorial/tutorial_3.png
|
|
|
+ :alt: Updated homepage
|
|
|
+
|
|
|
+A basic blog
|
|
|
+------------
|
|
|
+
|
|
|
+We are now ready to create a blog. To do so run
|
|
|
+``python manage.py startapp blog`` to create a new app in your Wagtail
|
|
|
+site.
|
|
|
+
|
|
|
+The following example defines a basic blog post model in
|
|
|
+``blog/models.py``
|
|
|
+
|
|
|
+.. code:: python
|
|
|
+
|
|
|
+ from django.db import models
|
|
|
+
|
|
|
+ from wagtail.wagtailcore.models import Page
|
|
|
+ from wagtail.wagtailcore.fields import RichTextField
|
|
|
+ from wagtail.wagtailadmin.edit_handlers import FieldPanel
|
|
|
+ from wagtail.wagtailsearch import index
|
|
|
+
|
|
|
+
|
|
|
+ class BlogPage(Page):
|
|
|
+ date = models.DateField("Post date")
|
|
|
+ intro = models.CharField(max_length=250)
|
|
|
+ body = RichTextField(blank=True)
|
|
|
+
|
|
|
+ search_fields = Page.search_fields + (
|
|
|
+ index.SearchField('intro'),
|
|
|
+ index.SearchField('body'),
|
|
|
+ )
|
|
|
+
|
|
|
+ content_panels = Page.content_panels + [
|
|
|
+ FieldPanel('title', classname="full title"),
|
|
|
+ FieldPanel('date'),
|
|
|
+ FieldPanel('intro'),
|
|
|
+ FieldPanel('body', classname="full")
|
|
|
+ ]
|
|
|
+
|
|
|
+Add the new ``blog`` app to ``INSTALLED_APPS`` in
|
|
|
+``mysite/settings/base.py``. Run ``python manage.py makemigrations`` and
|
|
|
+``python manage.py migrate``.
|
|
|
+
|
|
|
+.. figure:: ../_static/images/tutorial/tutorial_4.png
|
|
|
+ :alt: Create page screen
|
|
|
+
|
|
|
+.. figure:: ../_static/images/tutorial/tutorial_5.png
|
|
|
+ :alt: Page edit screen
|
|
|
+
|
|
|
+Image support
|
|
|
+~~~~~~~~~~~~~
|
|
|
+
|
|
|
+Wagtail provides support for images out of the box. To add them to your
|
|
|
+model:
|
|
|
+
|
|
|
+.. code:: python
|
|
|
+
|
|
|
+ from django.db import models
|
|
|
+
|
|
|
+ from wagtail.wagtailcore.models import Page
|
|
|
+ from wagtail.wagtailcore.fields import RichTextField
|
|
|
+ from wagtail.wagtailadmin.edit_handlers import FieldPanel
|
|
|
+ from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
|
|
|
+ from wagtail.wagtailsearch import index
|
|
|
+
|
|
|
+
|
|
|
+ class BlogPage(Page):
|
|
|
+ main_image = models.ForeignKey(
|
|
|
+ 'wagtailimages.Image',
|
|
|
+ null=True,
|
|
|
+ blank=True,
|
|
|
+ on_delete=models.SET_NULL,
|
|
|
+ related_name='+'
|
|
|
+ )
|
|
|
+ date = models.DateField("Post date")
|
|
|
+ intro = models.CharField(max_length=250)
|
|
|
+ body = RichTextField(blank=True)
|
|
|
+
|
|
|
+ search_fields = Page.search_fields + (
|
|
|
+ index.SearchField('intro'),
|
|
|
+ index.SearchField('body'),
|
|
|
+ )
|
|
|
+
|
|
|
+ content_panels = Page.content_panels + [
|
|
|
+ FieldPanel('title', classname="full title"),
|
|
|
+ FieldPanel('date'),
|
|
|
+ ImageChooserPanel('main_image'),
|
|
|
+ FieldPanel('intro'),
|
|
|
+ FieldPanel('body'),
|
|
|
+ ]
|
|
|
+
|
|
|
+Adjust your BlogPage template to output the image:
|
|
|
+
|
|
|
+.. code:: html+django
|
|
|
+
|
|
|
+ {% extends "base.html" %}
|
|
|
+
|
|
|
+ {% load static core_tags wagtailcore_tags wagtailimages_tags %}
|
|
|
+
|
|
|
+ {% block body_class %}template-{{ self.get_verbose_name|slugify }}{% endblock %}
|
|
|
+
|
|
|
+ {% block content %}
|
|
|
+ <h1>{{ self.title }}</h1>
|
|
|
+ <p class="meta">{{ self.date }}</p>
|
|
|
+
|
|
|
+ {% if self.main_image %}
|
|
|
+ {% image self.main_image width-400 %}
|
|
|
+ {% endif %}
|
|
|
+
|
|
|
+ <div class="intro">{% self.intro %}</div>
|
|
|
+
|
|
|
+ {{ self.body | richtext }}
|
|
|
+
|
|
|
+ {% endblock %}
|
|
|
+
|
|
|
+.. figure:: ../_static/images/tutorial/tutorial_6.png
|
|
|
+ :alt: A blog post sample
|
|
|
+
|
|
|
+You can read more about using images in templates in the
|
|
|
+:doc:`docs <../topics/images>`.
|
|
|
+
|
|
|
+Blog Index
|
|
|
+~~~~~~~~~~
|
|
|
+
|
|
|
+Let us extend the Blog app to provide an index.
|
|
|
+
|
|
|
+.. code:: python
|
|
|
+
|
|
|
+ class BlogIndexPage(Page):
|
|
|
+ intro = RichTextField(blank=True)
|
|
|
+
|
|
|
+ content_panels = Page.content_panels + [
|
|
|
+ FieldPanel('title', classname="full title"),
|
|
|
+ FieldPanel('intro', classname="full")
|
|
|
+ ]
|
|
|
+
|
|
|
+The above creates an index type to collect all our blog posts.
|
|
|
+
|
|
|
+``blog/templates/blog/blog_index_page.html``
|
|
|
+
|
|
|
+.. code:: html+django
|
|
|
+
|
|
|
+ {% extends "base.html" %}
|
|
|
+
|
|
|
+ {% load static core_tags wagtailcore_tags %}
|
|
|
+
|
|
|
+ {% block body_class %}template-{{ self.get_verbose_name|slugify }}{% endblock %}
|
|
|
+
|
|
|
+ {% block content %}
|
|
|
+ <h1>{{ self.title }}</h1>
|
|
|
+
|
|
|
+ <div class="intro">{{ self.intro }}</div>
|
|
|
+ {% endblock %}
|
|
|
+
|
|
|
+Related items
|
|
|
+~~~~~~~~~~~~~
|
|
|
+
|
|
|
+Let's extend the BlogIndexPage to add related links. The related links
|
|
|
+can be BlogPages or external links. Change ``blog/models.py`` to
|
|
|
+
|
|
|
+.. code:: python
|
|
|
+
|
|
|
+ from django.db import models
|
|
|
+
|
|
|
+ from modelcluster.fields import ParentalKey
|
|
|
+
|
|
|
+ from wagtail.wagtailcore.models import Page, Orderable
|
|
|
+ from wagtail.wagtailcore.fields import RichTextField
|
|
|
+ from wagtail.wagtailadmin.edit_handlers import (FieldPanel,
|
|
|
+ InlinePanel,
|
|
|
+ MultiFieldPanel,
|
|
|
+ PageChooserPanel)
|
|
|
+ from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
|
|
|
+ from wagtail.wagtailsearch import index
|
|
|
+
|
|
|
+
|
|
|
+ class BlogPage(Page):
|
|
|
+ main_image = models.ForeignKey(
|
|
|
+ 'wagtailimages.Image',
|
|
|
+ null=True, blank=True,
|
|
|
+ on_delete=models.SET_NULL,
|
|
|
+ related_name='+'
|
|
|
+ )
|
|
|
+ date = models.DateField("Post date")
|
|
|
+ intro = models.CharField(max_length=250)
|
|
|
+ body = RichTextField(blank=True)
|
|
|
+
|
|
|
+ search_fields = Page.search_fields + (
|
|
|
+ index.SearchField('intro'),
|
|
|
+ index.SearchField('body'),
|
|
|
+ )
|
|
|
+
|
|
|
+ content_panels = Page.content_panels + [
|
|
|
+ FieldPanel('title', classname="full title"),
|
|
|
+ FieldPanel('date'),
|
|
|
+ ImageChooserPanel('main_image'),
|
|
|
+ FieldPanel('intro'),
|
|
|
+ FieldPanel('body'),
|
|
|
+ ]
|
|
|
+
|
|
|
+
|
|
|
+ class LinkFields(models.Model):
|
|
|
+ link_external = models.URLField("External link", blank=True)
|
|
|
+ link_page = models.ForeignKey(
|
|
|
+ 'wagtailcore.Page',
|
|
|
+ null=True,
|
|
|
+ blank=True,
|
|
|
+ related_name='+'
|
|
|
+ )
|
|
|
+
|
|
|
+ @property
|
|
|
+ def link(self):
|
|
|
+ if self.link_page:
|
|
|
+ return self.link_page.url
|
|
|
+ else:
|
|
|
+ return self.link_external
|
|
|
+
|
|
|
+ panels = [
|
|
|
+ FieldPanel('link_external'),
|
|
|
+ PageChooserPanel('link_page'),
|
|
|
+ ]
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ abstract = True
|
|
|
+
|
|
|
+
|
|
|
+ # Related links
|
|
|
+
|
|
|
+ class RelatedLink(LinkFields):
|
|
|
+ title = models.CharField(max_length=255, help_text="Link title")
|
|
|
+
|
|
|
+ panels = [
|
|
|
+ FieldPanel('title'),
|
|
|
+ MultiFieldPanel(LinkFields.panels, "Link"),
|
|
|
+ ]
|
|
|
+
|
|
|
+ class Meta:
|
|
|
+ abstract = True
|
|
|
+
|
|
|
+
|
|
|
+ class BlogIndexRelatedLink(Orderable, RelatedLink):
|
|
|
+ page = ParentalKey('BlogIndexPage', related_name='related_links')
|
|
|
+
|
|
|
+
|
|
|
+ class BlogIndexPage(Page):
|
|
|
+ intro = RichTextField(blank=True)
|
|
|
+
|
|
|
+ content_panels = Page.content_panels + [
|
|
|
+ FieldPanel('title', classname="full title"),
|
|
|
+ InlinePanel('related_links', label="Related links"),
|
|
|
+ ]
|
|
|
+
|
|
|
+.. figure:: ../_static/images/tutorial/tutorial_7.png
|
|
|
+ :alt: Blog index edit screen
|
|
|
+
|
|
|
+Extend ``blog_index_page.html`` to show related items
|
|
|
+
|
|
|
+.. code:: html+django
|
|
|
+
|
|
|
+ {% extends "base.html" %}
|
|
|
+
|
|
|
+ {% load static core_tags wagtailcore_tags %}
|
|
|
+
|
|
|
+ {% block body_class %}template-{{ self.get_verbose_name|slugify }}{% endblock %}
|
|
|
+
|
|
|
+ {% block content %}
|
|
|
+ <h1>{{ self.title }}</h1>
|
|
|
+
|
|
|
+ <div class="intro">{{ self.intro }}</div>
|
|
|
+
|
|
|
+ {% if self.related_links.all %}
|
|
|
+ <ul>
|
|
|
+ {% for item in self.related_links.all %}
|
|
|
+ <li><a href="{{ item.link }}">{{ item.title }}</a></li>
|
|
|
+ {% endfor %}
|
|
|
+ </ul>
|
|
|
+ {% endif %}
|
|
|
+ {% endblock %}
|
|
|
+
|
|
|
+You now have a fully working blog with featured blog posts.
|
|
|
+
|
|
|
+.. figure:: ../_static/images/tutorial/tutorial_8.png
|
|
|
+ :alt: Barebones blog index
|
|
|
+
|
|
|
+Where next
|
|
|
+----------
|
|
|
+
|
|
|
+- Read the Wagtail :doc:`topics <../topics/index>` and :doc:`reference <../reference/index>` documentation
|
|
|
+- Learn how to implement :doc:`StreamField <../topics/streamfield>` for freeform page content
|
|
|
+- Browse through the :doc:`advanced topics <../advanced_topics/index>` section and read :doc:`third-party tutorials <../advanced_topics/third_party_tutorials>`
|