diff --git a/docs/_static/images/tutorial/tutorial_1.png b/docs/_static/images/tutorial/tutorial_1.png new file mode 100644 index 0000000000..4d92ccd3b1 Binary files /dev/null and b/docs/_static/images/tutorial/tutorial_1.png differ diff --git a/docs/_static/images/tutorial/tutorial_2.png b/docs/_static/images/tutorial/tutorial_2.png new file mode 100644 index 0000000000..3ea4ad6468 Binary files /dev/null and b/docs/_static/images/tutorial/tutorial_2.png differ diff --git a/docs/_static/images/tutorial/tutorial_3.png b/docs/_static/images/tutorial/tutorial_3.png new file mode 100644 index 0000000000..d769007538 Binary files /dev/null and b/docs/_static/images/tutorial/tutorial_3.png differ diff --git a/docs/_static/images/tutorial/tutorial_4.png b/docs/_static/images/tutorial/tutorial_4.png new file mode 100644 index 0000000000..906471d482 Binary files /dev/null and b/docs/_static/images/tutorial/tutorial_4.png differ diff --git a/docs/_static/images/tutorial/tutorial_5.png b/docs/_static/images/tutorial/tutorial_5.png new file mode 100644 index 0000000000..8535592d50 Binary files /dev/null and b/docs/_static/images/tutorial/tutorial_5.png differ diff --git a/docs/_static/images/tutorial/tutorial_6.png b/docs/_static/images/tutorial/tutorial_6.png new file mode 100644 index 0000000000..59489bc38d Binary files /dev/null and b/docs/_static/images/tutorial/tutorial_6.png differ diff --git a/docs/_static/images/tutorial/tutorial_7.png b/docs/_static/images/tutorial/tutorial_7.png new file mode 100644 index 0000000000..67808daafb Binary files /dev/null and b/docs/_static/images/tutorial/tutorial_7.png differ diff --git a/docs/_static/images/tutorial/tutorial_8.png b/docs/_static/images/tutorial/tutorial_8.png new file mode 100644 index 0000000000..a0d6fb31f6 Binary files /dev/null and b/docs/_static/images/tutorial/tutorial_8.png differ diff --git a/docs/getting_started/index.rst b/docs/getting_started/index.rst index c1342513af..6be616b7ce 100644 --- a/docs/getting_started/index.rst +++ b/docs/getting_started/index.rst @@ -43,4 +43,5 @@ There are a few optional packages which are not installed by default but are rec .. toctree:: :maxdepth: 2 + tutorial demo_site diff --git a/docs/getting_started/tutorial.rst b/docs/getting_started/tutorial.rst new file mode 100644 index 0000000000..7452b9accd --- /dev/null +++ b/docs/getting_started/tutorial.rst @@ -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 %} +

{{ self.title }}

+

{{ self.date }}

+ + {% if self.main_image %} + {% image self.main_image width-400 %} + {% endif %} + +
{% self.intro %}
+ + {{ 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 %} +

{{ self.title }}

+ +
{{ self.intro }}
+ {% 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 %} +

{{ self.title }}

+ +
{{ self.intro }}
+ + {% if self.related_links.all %} + + {% 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>` diff --git a/docs/index.rst b/docs/index.rst index c632a15c7a..5cada3729b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,6 +9,7 @@ Below are some useful links to help you get started with Wagtail. * **First steps** * :doc:`getting_started/index` + * :doc:`getting_started/tutorial` * :doc:`getting_started/demo_site` * **Creating your Wagtail site**