$30 off During Our Annual Pro Sale. View Details »

What the Wagtail Docs Don't Tell You

What the Wagtail Docs Don't Tell You

The importance of the Page model, saving Pages and Redirects programmatically, and doing more with the User model!

Lacey Williams Henschel

June 14, 2018
Tweet

More Decks by Lacey Williams Henschel

Other Decks in Programming

Transcript

  1. What the Wagtail Docs
    Don't Tell You
    Lacey Williams Henschel
    Wagtail Space US 2018
    !
    @laceynwilliams | @revsys

    View Slide

  2. @laceynwilliams | @revsys

    View Slide

  3. @laceynwilliams | @revsys

    View Slide

  4. the docs are great
    ✓ upgrading
    ✓ ge*ng started
    ✓ end users
    @laceynwilliams | @revsys

    View Slide

  5. ... but there are some possibili0es for sprint
    projects in there too!
    @laceynwilliams | @revsys

    View Slide

  6. I'm a liar.
    @laceynwilliams | Source:
    pinocchiomuseum.org

    View Slide

  7. the page model
    @laceynwilliams | Source: Syd Wachs via Unsplash

    View Slide

  8. bit.ly/wagtail-page-docs
    bit.ly/wagtail-page-code
    @laceynwilliams | @revsys

    View Slide

  9. why do I care about the Page class?
    ✓ avoid duplica+on
    ✓ helpful methods
    ✓ dealing with Pages programma+cally
    @laceynwilliams | @revsys

    View Slide

  10. class Page(AbstractPage):
    title = models.CharField()
    draft_title = models.CharField()
    slug = models.SlugField()
    live = models.BooleanField()
    url_path = models.TextField()
    owner = models.ForeignKey(settings.AUTH_USER_MODEL)
    seo_title = models.CharField()
    show_in_menus = models.BooleanField()
    search_description = models.TextField()
    go_live_at = models.DateTimeField()
    expire_at = models.DateTimeField()
    expired = models.BooleanField()
    locked = models.BooleanField()
    first_published_at = models.DateTimeField()
    last_published_at = models.DateTimeField()
    latest_revision_created_at = models.DateTimeField()
    live_revision = models.ForeignKey()
    @laceynwilliams | @revsys

    View Slide

  11. class Page(AbstractPage):
    title = models.CharField()
    draft_title = models.CharField()
    slug = models.SlugField()
    live = models.BooleanField()
    url_path = models.TextField()
    owner = models.ForeignKey(settings.AUTH_USER_MODEL)
    seo_title = models.CharField()
    show_in_menus = models.BooleanField()
    search_description = models.TextField()
    go_live_at = models.DateTimeField()
    expire_at = models.DateTimeField()
    expired = models.BooleanField()
    locked = models.BooleanField()
    first_published_at = models.DateTimeField()
    last_published_at = models.DateTimeField()
    latest_revision_created_at = models.DateTimeField()
    live_revision = models.ForeignKey()
    @laceynwilliams | @revsys

    View Slide

  12. @laceynwilliams | Source: Wagtail docs

    View Slide

  13. class Page(AbstractPage):
    title = models.CharField()
    draft_title = models.CharField()
    slug = models.SlugField()
    live = models.BooleanField()
    url_path = models.TextField()
    owner = models.ForeignKey(settings.AUTH_USER_MODEL)
    seo_title = models.CharField()
    show_in_menus = models.BooleanField()
    search_description = models.TextField()
    go_live_at = models.DateTimeField()
    expire_at = models.DateTimeField()
    expired = models.BooleanField()
    locked = models.BooleanField()
    first_published_at = models.DateTimeField()
    last_published_at = models.DateTimeField()
    latest_revision_created_at = models.DateTimeField()
    live_revision = models.ForeignKey()
    @laceynwilliams | @revsys

    View Slide

  14. @laceynwilliams | Source: Screenshot

    View Slide

  15. class Page(AbstractPage):
    title = models.CharField()
    draft_title = models.CharField()
    slug = models.SlugField()
    live = models.BooleanField()
    url_path = models.TextField()
    owner = models.ForeignKey(settings.AUTH_USER_MODEL)
    seo_title = models.CharField()
    show_in_menus = models.BooleanField()
    search_description = models.TextField()
    go_live_at = models.DateTimeField()
    expire_at = models.DateTimeField()
    expired = models.BooleanField()
    locked = models.BooleanField()
    first_published_at = models.DateTimeField()
    last_published_at = models.DateTimeField()
    latest_revision_created_at = models.DateTimeField()
    live_revision = models.ForeignKey()
    @laceynwilliams | @revsys

    View Slide

  16. class Page(AbstractPage):
    """ Wildly truncated """
    def save_revision(self, user=None, submitted_for_moderation=False,
    approved_go_live_at=None, changed=True):
    ...
    revision = self.revisions.create(
    content_json=self.to_json(),
    user=user,
    submitted_for_moderation=submitted_for_moderation,
    approved_go_live_at=approved_go_live_at,
    )
    ...
    return revision
    @laceynwilliams | @revsys

    View Slide

  17. sprint ideas
    1. Expand on "Page Models" in Usage Guide?
    @laceynwilliams | @revsys

    View Slide

  18. parent/child
    page types
    @laceynwilliams | Source: Harshil Gudka via Unsplash

    View Slide

  19. bit.ly/wagtail-page-rules
    @laceynwilliams | @revsys

    View Slide

  20. @laceynwilliams | @revsys

    View Slide

  21. ...but it's right there in the docs
    ✓ Docs were a li,le too light for me
    ✓ This rela3onship is important
    @laceynwilliams | @revsys

    View Slide

  22. from wagtail.core.models import Page
    class PostIndexPage(Page):
    # some attributes...
    parent_page_types = ['homepage.Homepage']
    subpage_types = ['posts.PostPage']
    class PostPage(Page):
    # some attributes...
    parent_page_types = ['posts.PostIndexPage']
    @laceynwilliams | @revsys

    View Slide

  23. parent_page_types
    What pages can create this kind of page?
    Who can your parents be?
    @laceynwilliams | @revsys

    View Slide

  24. subpage_types
    What pages can this page create?
    Who can your children be?
    @laceynwilliams | @revsys

    View Slide

  25. from wagtail.core.models import Page
    class PostIndexPage(Page):
    # some attributes...
    parent_page_types = ['homepage.Homepage']
    subpage_types = ['posts.PostPage']
    class PostPage(Page):
    # some attributes...
    parent_page_types = ['posts.PostIndexPage']
    @laceynwilliams | @revsys

    View Slide

  26. from wagtail.core.models import Page
    class PostIndexPage(Page):
    # some attributes...
    parent_page_types = ['homepage.Homepage']
    subpage_types = ['posts.PostPage']
    class PostPage(Page):
    # some attributes...
    parent_page_types = ['posts.PostIndexPage']
    @laceynwilliams | @revsys

    View Slide

  27. from wagtail.core.models import Page
    class PostIndexPage(Page):
    # some attributes...
    parent_page_types = ['homepage.Homepage']
    subpage_types = ['posts.PostPage']
    class PostPage(Page):
    # some attributes...
    parent_page_types = ['posts.PostIndexPage']
    @laceynwilliams | @revsys

    View Slide

  28. from wagtail.core.models import Page
    class PostIndexPage(Page):
    # some attributes...
    parent_page_types = ['homepage.Homepage']
    subpage_types = ['posts.PostPage']
    class PostPage(Page):
    # some attributes...
    parent_page_types = ['posts.PostIndexPage']
    @laceynwilliams | @revsys

    View Slide

  29. If you want to limit a page
    to have only one type of child
    and limit its child
    to have only one type of parent
    you have to tell both models.
    @laceynwilliams | @revsys

    View Slide

  30. sprint ideas
    1. Expand on "Page Models" in Usage Guide?
    2. Add example to "Parent page / subpage type rules"?
    @laceynwilliams | @revsys

    View Slide

  31. DIY page
    saving
    @laceynwilliams | Source: Jasmin Schreiber via Unsplash

    View Slide

  32. why do I care about saving pages?
    ✓ content migra,ons
    ✓ backda,ng
    ✓ making changes to pages outside the UI
    @laceynwilliams | @revsys

    View Slide

  33. assumptions
    ✓ Model
    PostPage
    ✓ Index page
    PostIndexPage
    ✓ All data is valid
    @laceynwilliams | @revsys

    View Slide

  34. from .models import PostPage, PostIndexPage
    def create_new_post(fields):
    post = PostPage()
    post.title = fields['title']
    post.latest_revision_created_at = fields['latest_revision_created_at']
    post.first_published_at = fields['first_published_at']
    # rest of fields
    post_index = PostIndexPage.objects.get(slug='posts')
    post_index.add_child(instance=post)
    revision = post.save_revision(submitted_for_moderation=False)
    post.save()
    if post.live:
    revision.publish()
    return post
    @laceynwilliams | @revsys

    View Slide

  35. from .models import PostPage, PostIndexPage
    def create_new_post(fields):
    post = PostPage()
    post.title = fields['title']
    post.latest_revision_created_at = fields['latest_revision_created_at']
    post.first_published_at = fields['first_published_at']
    # rest of fields
    post_index = PostIndexPage.objects.get(slug='posts')
    post_index.add_child(instance=post)
    revision = post.save_revision(submitted_for_moderation=False)
    post.save()
    if post.live:
    revision.publish()
    return post
    @laceynwilliams | @revsys

    View Slide

  36. from .models import PostPage, PostIndexPage
    def create_new_post(fields):
    post = PostPage()
    post.title = fields['title']
    post.latest_revision_created_at = fields['latest_revision_created_at']
    post.first_published_at = fields['first_published_at']
    # rest of fields
    post_index = PostIndexPage.objects.get(slug='posts')
    post_index.add_child(instance=post)
    revision = post.save_revision(submitted_for_moderation=False)
    post.save()
    if post.live:
    revision.publish()
    return post
    @laceynwilliams | @revsys

    View Slide

  37. from .models import PostPage, PostIndexPage
    def create_new_post(fields):
    post = PostPage()
    post.title = fields['title']
    post.latest_revision_created_at = fields['latest_revision_created_at']
    post.first_published_at = fields['first_published_at']
    # rest of fields
    post_index = PostIndexPage.objects.get(slug='posts')
    post_index.add_child(instance=post)
    revision = post.save_revision(submitted_for_moderation=False)
    post.save()
    if post.live:
    revision.publish()
    return post
    @laceynwilliams | @revsys

    View Slide

  38. from .models import PostPage, PostIndexPage
    def create_new_post(fields):
    post = PostPage()
    post.title = fields['title']
    post.latest_revision_created_at = fields['latest_revision_created_at']
    post.first_published_at = fields['first_published_at']
    # rest of fields
    post_index = PostIndexPage.objects.get(slug='posts')
    post_index.add_child(instance=post)
    revision = post.save_revision(submitted_for_moderation=False)
    post.save()
    if post.live:
    revision.publish()
    return post
    @laceynwilliams | @revsys

    View Slide

  39. from .models import PostPage, PostIndexPage
    def create_new_post(fields):
    post = PostPage()
    post.title = fields['title']
    post.latest_revision_created_at = fields['latest_revision_created_at']
    post.first_published_at = fields['first_published_at']
    # rest of fields
    post_index = PostIndexPage.objects.get(slug='posts')
    post_index.add_child(instance=post)
    revision = post.save_revision(submitted_for_moderation=False)
    post.save()
    if post.live:
    revision.publish()
    return post
    @laceynwilliams | @revsys

    View Slide

  40. Is there a
    better way?
    @laceynwilliams | @revsys

    View Slide

  41. sprint ideas
    1. Expand on "Page Models" in Usage Guide?
    2. Add example to "Parent page / subpage type rules"?
    3. Method to add a new page instance? Or more docs?
    @laceynwilliams | @revsys

    View Slide

  42. DIY redirects
    @laceynwilliams | Source: Jamie Street via Unsplash

    View Slide

  43. why do I care about redirects?
    ✓ avoiding 404s in content migra1ons
    ✓ needing to add a lot of redirects
    @laceynwilliams | @revsys

    View Slide

  44. assumptions
    ✓ No custom Redirect class
    ✓ One site
    ✓ Redirects are permanent
    ✓ URL definitely 404s
    ✓ Redirec9ng to a Page instance
    @laceynwilliams | @revsys

    View Slide

  45. from wagtail.contrib.redirects.models import Redirect
    def add_redirect(old_url, post_page):
    redirect = Redirect()
    redirect.old_path = old_url.rstrip('/')
    redirect.is_permanent = True
    redirect.redirect_page = post_page
    redirect.save()
    return redirect
    @laceynwilliams | @revsys

    View Slide

  46. from wagtail.contrib.redirects.models import Redirect
    def add_redirect(old_url, post_page):
    redirect = Redirect()
    redirect.old_path = old_url.rstrip('/')
    redirect.is_permanent = True
    redirect.redirect_page = post_page
    redirect.save()
    return redirect
    @laceynwilliams | @revsys

    View Slide

  47. from wagtail.contrib.redirects.models import Redirect
    def add_redirect(old_url, post_page):
    redirect = Redirect()
    redirect.old_path = old_url.rstrip('/')
    redirect.is_permanent = True
    redirect.redirect_page = post_page
    redirect.save()
    return redirect
    @laceynwilliams | @revsys

    View Slide

  48. from wagtail.contrib.redirects.models import Redirect
    def add_redirect(old_url, post_page):
    redirect = Redirect()
    redirect.old_path = old_url.rstrip('/')
    redirect.is_permanent = True
    redirect.redirect_page = post_page
    redirect.save()
    return redirect
    @laceynwilliams | @revsys

    View Slide

  49. from wagtail.contrib.redirects.models import Redirect
    def add_redirect(old_url, post_page):
    redirect = Redirect()
    redirect.old_path = old_url.rstrip('/')
    redirect.is_permanent = True
    redirect.redirect_page = post_page
    redirect.save()
    return redirect
    @laceynwilliams | @revsys

    View Slide

  50. from wagtail.contrib.redirects.models import Redirect
    def add_redirect(old_url, post_page):
    redirect = Redirect()
    redirect.old_path = old_url.rstrip('/')
    redirect.is_permanent = True
    redirect.redirect_page = post_page
    redirect.save()
    return redirect
    @laceynwilliams | @revsys

    View Slide

  51. sprint ideas
    1. Expand on "Page Models" in Usage Guide?
    2. Add example to "Parent page / subpage type rules"?
    3. Method to add a new page instance? Or more docs?
    4. Method to add redirect programmatically?
    @laceynwilliams | @revsys

    View Slide

  52. using the User
    model
    @laceynwilliams | Source: WOCinTech Chat

    View Slide

  53. bit.ly/wagtail-custom-user
    @laceynwilliams | @revsys

    View Slide

  54. why do I care about the User model?
    ✓ People
    ✓ Easy access in admin
    @laceynwilliams | @revsys

    View Slide

  55. '

    View Slide

  56. # team/models.py
    from django.contrib.auth import get_user_model
    from wagtail.core.models import Page
    User = get_user_model()
    class TeamMemberPage(Page):
    ...
    user = models.ForeignKey(User)
    @laceynwilliams | @revsys

    View Slide

  57. # team/models.py
    from django.contrib.auth import get_user_model
    from wagtail.core.models import Page
    User = get_user_model()
    class TeamMemberPage(Page):
    ...
    user = models.ForeignKey(User)
    @laceynwilliams | @revsys

    View Slide

  58. # team/models.py
    from django.contrib.auth import get_user_model
    from wagtail.core.models import Page
    User = get_user_model()
    class TeamMemberPage(Page):
    ...
    user = models.ForeignKey(User)
    @laceynwilliams | @revsys

    View Slide

  59. So then just
    ForeignKey
    directly to
    TeamMemberPage
    from
    PostPage
    , right?
    @laceynwilliams | @revsys

    View Slide

  60. # posts/models.py
    from django.contrib.auth import get_user_model
    from wagtail.core.models import Page
    User = get_user_model()
    class PostPage(Page):
    author = models.ForeignKey(User)
    # or
    author = models.ForeignKey('team.TeamMemberPage')
    @laceynwilliams | @revsys

    View Slide

  61. @laceynwilliams | Source: NeONBRAND via Unsplash

    View Slide

  62. # posts/models.py
    from wagtail.admin.edit_handlers import PageChooserPanel
    from wagtail.core.models import Page
    class PostPage(Page):
    author = models.ForeignKey('wagtailcore.Page')
    content_panels = Page.content_panels + [
    PageChooserPanel('author', ['team.TeamMemberPage']),
    ]
    @laceynwilliams | @revsys

    View Slide

  63. # posts/models.py
    from wagtail.admin.edit_handlers import PageChooserPanel
    from wagtail.core.models import Page
    class PostPage(Page):
    author = models.ForeignKey('wagtailcore.Page')
    content_panels = Page.content_panels + [
    PageChooserPanel('author', ['team.TeamMemberPage']),
    ]
    @laceynwilliams | @revsys

    View Slide

  64. # posts/models.py
    from wagtail.admin.edit_handlers import PageChooserPanel
    from wagtail.core.models import Page
    class PostPage(Page):
    author = models.ForeignKey('wagtailcore.Page')
    content_panels = Page.content_panels + [
    PageChooserPanel('author', ['team.TeamMemberPage']),
    ]
    @laceynwilliams | @revsys

    View Slide

  65. sprint ideas
    1. Expand on "Page Models" in Usage Guide?
    2. Add example to "Parent page / subpage type rules"?
    3. Method to add a new page instance? Or more docs?
    4. Method to add redirect programmatically?
    5. Expand on User model docs?
    @laceynwilliams | @revsys

    View Slide

  66. Look again at those docs.
    That's here. That's home. That's us.
    — Carl Sagan, probably
    @laceynwilliams | Source: Ma4eo Fusco via Unsplash

    View Slide

  67. !
    @laceynwilliams
    !
    [email protected]
    !
    revsys.com
    !
    laceyhenschel.com
    @laceynwilliams | @revsys

    View Slide