Skip to content

Instantly share code, notes, and snippets.

@kevinhowbrook
Last active March 26, 2019 18:44
Show Gist options
  • Save kevinhowbrook/d2f1ff081a437da4156fc6742a424c2a to your computer and use it in GitHub Desktop.
Save kevinhowbrook/d2f1ff081a437da4156fc6742a424c2a to your computer and use it in GitHub Desktop.
Wagtail local process tips

pipenv for function/code completion

When using vagrant, vscode won't be able to map to the vm, well not yet anyway. A way around this is to checkout a branch called pipenv, run pipenv --python 3 to create a virtual environment. Then run pipenv install -r requirements.txt to install everything in the project.

After doing this, in vscode ctrl+p then search for >python: select interpreter. Select the virtual env python, it will look something like this: /home/yourname/.local/share/virtualenvs/project-foo-JbRjzqlX/bin/python.

Just don't commit the pipenv stuff.

Flake8

Place the following in .git/hooks/pre-commit

#!/usr/bin/python
import sys

from flake8.main import git

if __name__ == '__main__':
    sys.exit(
        git.hook(
            strict=git.config_for('strict'),
            lazy=git.config_for('lazy'),
        )
    )

Then to stop the commit due to errors run git config --bool flake8.strict true

If there is a setup.cfg in the folder then flake8 should use those settings.

You can also set up vscode to run flake8 in your settings:

"python.linting.flake8Enabled": true,
"python.linting.flake8Args": [
    "--config=setup.cfg"
],

Isort

isort --check-only --diff --recursive --skip-glob "*/migrations/*" --skip-glob "*/node_modules/*"

or to fix

isort --recursive --skip-glob "*/migrations/*" --skip-glob "*/node_modules/*"

or just isort --recursive [dir]

Migrations

Naming the migrations ./manage.py makemigrations -n name_for_my_migrations.

Simple tag

@register.simple_tag()
def parent_page(page):
    parent_page = BasePage.objects.live().parent_of(page).not_type(HomePage).first()
    return parent_page

The return value is a dictionary {} Then in the template

{% parent_page page as parent_page %}
{% if parent_page %}
    <a href="{% pageurl parent_page %}">
        {{ parent_page }}
    </a>
{% endif %}

More here in the django docs

Inclusion tag

This is similar to a simple tag, only it works with a template. It's really handy for repeated sections like grids and cards

@register.inclusion_tag('my/template/for/this.html', takes_context=True)
def xfiles(context, page):

    if page is None:
        return {}

    page = page.specific
    # Logic here for setting values...
    title = page.listing_title or page.title
    agent = 'Fox Mulder'
    
    return {
        'agent': agent,
        'title': title
    }

Then in wherever you wanted to use this:

{% load whatever_file_the_tag_was_in_tags %}
{% xfiles page %} # page will be passed through to your template_tag

Limit child page creation to 3 level

@classmethod
    def can_create_at(cls, parent):
    #  Limits child creation to 3 levels
    return (
        super().can_create_at(parent) and
        not isinstance(parent.get_parent().get_parent().specific, cls)
    )

Overriding / adding taxonomy fields

class NewsType(models.Model):
    title = models.CharField(max_length=128)
    description = models.TextField(blank=True)
    listing_image = models.ForeignKey(
        'images.CustomImage',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )
    
    panels = [
        FieldPanel('title'),
        FieldPanel('description'),
        ImageChooserPanel('listing_image'),
    ]

    def __str__(self):
        return self.title

Adding django debug toolbar

It is optional even for the dev env, becuase it slows requests down. Person who wants to enable it should install into the project's environment manually

in settings/dev.py add:

# Adds Django Debug Toolbar, if preset
try:
    import debug_toolbar  # noqa
    INSTALLED_APPS.append('debug_toolbar')  # noqa
    MIDDLEWARE.insert(0, 'debug_toolbar.middleware.DebugToolbarMiddleware')  # noqa
except ImportError:
    pass

in [project]/urls.py add the folling in if settings.DEBUG:

 # Try to install the django debug toolbar, if exists
    if apps.is_installed('debug_toolbar'):
        import debug_toolbar
        urlpatterns = [
            path('__debug__/', include(debug_toolbar.urls)),
        ] + urlpatterns

Improve DB queries for performance

see Mikalais pr notes here

Django ORM cheetsheet

https://amitness.com/2018/10/django-orm-for-sql-users/

Correcting bad markup

from wagtail.admin.rich_text.converters.editor_html import EditorHTMLConverter

converter = EditorHTMLConverter(features=['h2', 'h3', 'h4', 'bold', 'italic', 'ol', 'ul', 'link'])

# then, for each bit of rich text content that needs cleaning:
clean_html = converter.to_database_format(old_html)

Passing URL params through to tpl

In the model/view:

Then in the template:

 <a href="{% pageurl view_more_page %}{% if extra_url_params %}?{{ extra_url_params }}{% endif %}" class="button button-ghost-reverse button-ghost-reverse--black">View More</a>

Overriding admin templates

Say you want to override something in the admin UI. Basicall look for it in wagail and figure out when it is you want to override. eg snippets table is at https://github.com/wagtail/wagtail/blob/master/wagtail/snippets/templates/wagtailsnippets/snippets/list.html

Then in your_app/templates/wagtailsnippets/snippets/list.html add and edit the template

Validating a related model with a boolean condition

```if cleaned_data['sponsored_content'] and len(self.formsets['sponsor_placements'].ordered_forms) < 1:`` len(self.formsets['sponsor_placements']) will yield the stale form data

Bugs and fixes

_sqlite3 not found (ubuntu)

Ensure sqlite is installed ssudo apt-get install libsqlite3-dev Then reompile python, if using pyenv, uninstall then install your version again

Writing tests

Exmaple of a test created under a parent

from wagtail.tests.utils import WagtailPageTests
from gallery.models import GalleryPage
from home.models import HomePage
from standardpages.models import IndexPage
from images.models import CustomImage
from wagtail.images.tests.utils import get_test_image_file
from wagtail.tests.utils.form_data import (
   streamfield, nested_form_data)


class GalleryPageTests(WagtailPageTests):
   def setUp(self):
       super().setUp()
       home_page = HomePage.objects.get(pk=3)
       page = IndexPage(
           slug='foobar',
           title='Foo Bar'
       )
       home_page.add_child(instance=page)
       page.save()
       self.image = CustomImage.objects.create(
           title='Test image',
           width=100,
           height=100,
           file=get_test_image_file())
       self.index_page = IndexPage.objects.get(title="Foo Bar")
       self.data = nested_form_data({
           'title': 'A Gallery',
           'slides': streamfield([
               ('image_slide', {
                   'slide_title': 'Image slide title',
                   'slide_image': {
                       'image': self.image.id
                   },
               }),
               ('video_slide', {
                   'slide_title': 'Video slide title',
                   'video': 'https://youtu.be/iWh385F5lms',
                   'default_image': {
                       'image': self.image.id
                   },
               }),
               ('audio_slide', {
                   'slide_title': 'Audio slide title',
                   'audio': 'https://soundcloud.com/radiolab/shorts-double-blasted',
                   'default_image': {
                       'image': self.image.id
                   },
               })
           ]),
       })

   def test_can_create_a_page(self):
       self.assertCanCreateAt(HomePage, GalleryPage)
       self.assertCanCreateAt(IndexPage, GalleryPage)

   def test_can_create_content_page(self):
       self.assertCanCreate(self.index_page, GalleryPage, self.data)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment