Explore all Wagtail open source software, libraries, packages, source code, cloud functions and APIs.

Popular New Releases in Wagtail

coderedcms

0.22.3

draftail

v1.4.1

draft-convert

v2.1.12

formeo

v1.6.2

wagtail-bootstrap-blog

v1.0.0

Popular Libraries in Wagtail

awesome-wagtail

by springload doticonpythondoticon

star image 1450 doticon

A curated list of awesome packages, articles, and other cool resources from the Wagtail community.

acme

by ietf-wg-acme doticonpythondoticon

star image 882 doticon

A protocol for automating certificate issuance

django-recaptcha

by praekelt doticonpythondoticon

star image 761 doticonBSD-3-Clause

Django reCAPTCHA form field/widget integration app.

bakerydemo

by wagtail doticonpythondoticon

star image 563 doticonBSD-3-Clause

Next generation Wagtail demo, born in Reykjavik

coderedcms

by coderedcorp doticonpythondoticon

star image 498 doticonBSD-3-Clause

A content management system for marketing websites based on Django and Wagtail.

draftail

by springload doticonjavascriptdoticon

star image 485 doticonMIT

📝🍸 A configurable rich text editor built with Draft.js

puput

by APSL doticonpythondoticon

star image 481 doticonMIT

A Django blog app implemented in Wagtail

draft-wysiwyg

by bkniffler doticonjavascriptdoticon

star image 454 doticonMIT

Draft-JS experiments with drag&drop, resizing, tooltips, WIP

draft-convert

by HubSpot doticonjavascriptdoticon

star image 444 doticonApache-2.0

Extensibly serialize & deserialize Draft.js ContentState with HTML.

Trending New libraries in Wagtail

django-salesman

by dinoperovic doticonpythondoticon

star image 209 doticonBSD-3-Clause

Headless e-commerce framework for Django.

editnerf

by stevliu doticonpythondoticon

star image 108 doticonMIT

Editing a Conditional Radiance Field

wagtail-birdsong

by neon-jungle doticonpythondoticon

star image 57 doticon

Create, send, preview, edit and test email campaigns from within Wagtail

gatsby-starter-builder

by BuilderIO doticonjavascriptdoticon

star image 56 doticonMIT

Gatsby starter with drag and drop page building

strapi-plugin-preview-content

by danestves doticonjavascriptdoticon

star image 43 doticonMIT

A plugin for Strapi Headless CMS that provides content preview to integrate with any frontend

docker-wagtail-develop

by wagtail doticonshelldoticon

star image 37 doticon

nhsx-website

by nhsx doticonpythondoticon

star image 32 doticonMIT

NHSX Website - built with Wagtail

wagtailsvg

by Aleksi44 doticonpythondoticon

star image 27 doticonGPL-3.0

Wagtail + SVG

wagtail-airtable

by wagtail doticonpythondoticon

star image 24 doticonBSD-3-Clause

Airtable import and export support for Wagtail pages and Django models.

Top Authors in Wagtail

1

wagtail

24 Libraries

star icon1970

2

torchbox

22 Libraries

star icon779

3

neon-jungle

17 Libraries

star icon411

4

MicrosoftDocs

15 Libraries

star icon32

5

springload

12 Libraries

star icon2215

6

thibaudcolas

11 Libraries

star icon132

7

ckeditor

8 Libraries

star icon170

8

cfpb

6 Libraries

star icon374

9

FlipperPA

6 Libraries

star icon202

10

bashu

5 Libraries

star icon89

1

24 Libraries

star icon1970

2

22 Libraries

star icon779

3

17 Libraries

star icon411

4

15 Libraries

star icon32

5

12 Libraries

star icon2215

6

11 Libraries

star icon132

7

8 Libraries

star icon170

8

6 Libraries

star icon374

9

6 Libraries

star icon202

10

5 Libraries

star icon89

Trending Kits in Wagtail

No Trending Kits are available at this moment for Wagtail

Trending Discussions on Wagtail

Snippet title problem after upgrade from wagtail 1.13 to 2.0

dependencies reference nonexistent child node

Wagtail Admin: How to Add ReadOnlyBlocks to show snippets fields values in a StructBlock

How to switch wagtail homepage depending on user logged in status

Adding external javascript snippet to a Wagtail page field

Changing Template Location in Wagtail

Wagtail 'View live' button provides wrong url after page creation while using id as slug

Testing a Wagtail StructBlock with a StreamBlockField

How to customize the admin form for a custom image model in Wagtail CMS?

Why won't a Wagtail TableBlock Display Properly in the Admin?

QUESTION

Snippet title problem after upgrade from wagtail 1.13 to 2.0

Asked 2022-Apr-11 at 11:13

I am working on wagtail upgrade on a site from version 1.13.4 . Was mostly following this post https://wagtail.org/blog/upgrading-wagtail/ . However, since the first update to wagtail 2.0 snippet titles are wrong. I could sort it out on the templated front end pages, but they still appear wrong on the admin panel.

How titles are displayed on the admin panel And this same issue is with fields that link to those snippets

I got as far as updating wagtail to 2.4, since the blog post said that it should support python 3.7, but issue still persists. As far as I can tell, there are no error messages on the terminal output, migrations run without any issues. Any clue where I should start looking?

This should be the relevant artist snippet declaration from the project

1@register_snippet
2class Artist(index.Indexed, models.Model):
3    class Meta:
4        ordering = ['name']
5
6    def __unicode__(self):
7        return unicode(self.name)
8
9    objects = ArtistQuerySet.as_manager()
10
11    name = models.CharField(max_length=512)
12    name_encoded = models.CharField(max_length=512)
13    image_url = models.URLField(blank=True, max_length=512)
14    image = models.ForeignKey(
15        'wagtailimages.Image',
16        blank=True,
17        null=True,
18        on_delete=models.SET_NULL,
19        related_name='+',
20    )
21
22    @property
23    def status(self):
24        # If there are no events currently attached to this artist
25        if not self.artistatevent_set.all():
26            return NO_CURRENT_DATES
27
28        # This artist does have some events, next see if there's any tickets
29        if self.total_ticket_count is not 0:
30
31            # Are they all the same type?
32            if self.available_ticket_count == self.total_ticket_count:
33                return AVAILABLE
34            if self.sold_out_ticket_count == self.total_ticket_count:
35                return SOLD_OUT
36            if self.cancelled_ticket_count == self.total_ticket_count:
37                return CANCELLED
38            if self.unavailable_ticket_count == self.total_ticket_count:
39                return UNAVAILABLE
40
41        # See if we have ANY of the following
42        if self.available_ticket_count:
43            return AVAILABLE
44        if self.coming_soon_ticket_count:
45            return COMING_SOON
46        if self.sold_out_ticket_count:
47            return SOLD_OUT
48
49        # Nothing matched, so default
50        return UNAVAILABLE
51
52    panels = [
53        FieldPanel('name'),
54        ImageChooserPanel('image'),
55    ]
56
57    search_fields = [
58        index.SearchField('name', partial_match=True),
59    ]
60
61
62

EDIT:

This is a snippet from admin.py declaration

1@register_snippet
2class Artist(index.Indexed, models.Model):
3    class Meta:
4        ordering = ['name']
5
6    def __unicode__(self):
7        return unicode(self.name)
8
9    objects = ArtistQuerySet.as_manager()
10
11    name = models.CharField(max_length=512)
12    name_encoded = models.CharField(max_length=512)
13    image_url = models.URLField(blank=True, max_length=512)
14    image = models.ForeignKey(
15        'wagtailimages.Image',
16        blank=True,
17        null=True,
18        on_delete=models.SET_NULL,
19        related_name='+',
20    )
21
22    @property
23    def status(self):
24        # If there are no events currently attached to this artist
25        if not self.artistatevent_set.all():
26            return NO_CURRENT_DATES
27
28        # This artist does have some events, next see if there's any tickets
29        if self.total_ticket_count is not 0:
30
31            # Are they all the same type?
32            if self.available_ticket_count == self.total_ticket_count:
33                return AVAILABLE
34            if self.sold_out_ticket_count == self.total_ticket_count:
35                return SOLD_OUT
36            if self.cancelled_ticket_count == self.total_ticket_count:
37                return CANCELLED
38            if self.unavailable_ticket_count == self.total_ticket_count:
39                return UNAVAILABLE
40
41        # See if we have ANY of the following
42        if self.available_ticket_count:
43            return AVAILABLE
44        if self.coming_soon_ticket_count:
45            return COMING_SOON
46        if self.sold_out_ticket_count:
47            return SOLD_OUT
48
49        # Nothing matched, so default
50        return UNAVAILABLE
51
52    panels = [
53        FieldPanel('name'),
54        ImageChooserPanel('image'),
55    ]
56
57    search_fields = [
58        index.SearchField('name', partial_match=True),
59    ]
60
61
62...
63class NameIDAdmin(admin.ModelAdmin):
64    list_display = ('name', 'id')
65...
66admin.site.register(Artist, NameIDAdmin)
67
68

ANSWER

Answered 2022-Apr-11 at 11:13

When upgrading from Python 2 to 3, you should replace the __unicode__ method with __str__: https://docs.djangoproject.com/en/1.10/topics/python3/#str-and-unicode-methods

Source https://stackoverflow.com/questions/71826673

QUESTION

dependencies reference nonexistent child node

Asked 2022-Apr-10 at 15:46

I tried to dockerize my Wagtail Web Application and this error Occurred. I tried docker-compose build there was no errors. after that i tried docker-compose up then this error occurred I really need help on this error. Error-

149a28e66a331_wagtail_landing_web_1 | django.db.migrations.exceptions.NodeNotFoundError: Migration home.0002_create_homepage dependencies reference nonexistent child node ('wagtailcore', '0053_locale_model')
249a28e66a331_wagtail_landing_web_1 exited with code 1
3

This is the Dockerfile this is the dockerfile of my wagtail site.I'm very new to wagtail and docker .Please guide me if there is an Error

149a28e66a331_wagtail_landing_web_1 | django.db.migrations.exceptions.NodeNotFoundError: Migration home.0002_create_homepage dependencies reference nonexistent child node ('wagtailcore', '0053_locale_model')
249a28e66a331_wagtail_landing_web_1 exited with code 1
3FROM python:3.8.1-slim-buster
4RUN useradd wagtail
5EXPOSE 80
6ENV PYTHONUNBUFFERED=1 \
7    PORT=80
8ENV PYTHONDONTWRITEBYTECODE 1
9RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-recommends \
10    build-essential \
11    libpq-dev \
12    libmariadbclient-dev \
13    libjpeg62-turbo-dev \
14    zlib1g-dev \
15    libwebp-dev \
16&& rm -rf /var/lib/apt/lists/*
17RUN pip install "gunicorn==20.0.4"
18
19COPY ./requirements.txt /requirements.txt
20RUN pip install -r /requirements.txt
21
22COPY ./compose/local/web/entrypoint /entrypoint
23RUN sed -i 's/\r$//g' /entrypoint
24RUN chmod +x /entrypoint
25
26COPY ./compose/local/web/start /start
27RUN sed -i 's/\r$//g' /start
28RUN chmod +x /start
29WORKDIR /app
30
31ENTRYPOINT ["/entrypoint"]
32

This is the YAML file This is the yaml file of the System

149a28e66a331_wagtail_landing_web_1 | django.db.migrations.exceptions.NodeNotFoundError: Migration home.0002_create_homepage dependencies reference nonexistent child node ('wagtailcore', '0053_locale_model')
249a28e66a331_wagtail_landing_web_1 exited with code 1
3FROM python:3.8.1-slim-buster
4RUN useradd wagtail
5EXPOSE 80
6ENV PYTHONUNBUFFERED=1 \
7    PORT=80
8ENV PYTHONDONTWRITEBYTECODE 1
9RUN apt-get update --yes --quiet && apt-get install --yes --quiet --no-install-recommends \
10    build-essential \
11    libpq-dev \
12    libmariadbclient-dev \
13    libjpeg62-turbo-dev \
14    zlib1g-dev \
15    libwebp-dev \
16&& rm -rf /var/lib/apt/lists/*
17RUN pip install "gunicorn==20.0.4"
18
19COPY ./requirements.txt /requirements.txt
20RUN pip install -r /requirements.txt
21
22COPY ./compose/local/web/entrypoint /entrypoint
23RUN sed -i 's/\r$//g' /entrypoint
24RUN chmod +x /entrypoint
25
26COPY ./compose/local/web/start /start
27RUN sed -i 's/\r$//g' /start
28RUN chmod +x /start
29WORKDIR /app
30
31ENTRYPOINT ["/entrypoint"]
32version: "3.7"
33
34services:
35  web:
36    build:
37      context: .
38      dockerfile: ./compose/local/web/Dockerfile
39    image: wagtail_bootstrap_blog_web
40    command: /start
41    volumes:
42      - .:/app
43    ports:
44      - 8000:8000
45    env_file:
46      - ./.env/.dev-sample
47    depends_on:
48      - db
49
50  db:
51    image: postgres:12.0-alpine
52    volumes:
53      - postgres_data:/var/lib/postgresql/data/
54    environment:
55      - POSTGRES_DB=wagtail_bootstrap_blog
56      - POSTGRES_USER=wagtail_bootstrap_blog
57      - POSTGRES_PASSWORD=wagtail_bootstrap_blog
58
59volumes:
60  postgres_data:
61 
62

ANSWER

Answered 2022-Apr-10 at 15:46

You didn't mention wagtail versions or if this is an upgrade. But that error sounds like this: https://docs.wagtail.org/en/stable/releases/2.11.3.html#run-before-declaration-needed-in-initial-homepage-migration

Source https://stackoverflow.com/questions/71816166

QUESTION

Wagtail Admin: How to Add ReadOnlyBlocks to show snippets fields values in a StructBlock

Asked 2022-Mar-04 at 12:41

I have a Snippet model(SampleSnippet) with fields field_a, field_b, and field_c. And I am using it in a StructBlock like:

1class SampleBlock(blocks.StructBlock):
2    service_snippet = SnippetChooserBlock(SampleSnippet)
3    ...
4
5

Now I want to show some of the SampleSnippet fields(field_a and field_b) in SampleBlock after saving the SampleBlock for the first time(after saving the snippet) on the wagtail admin site. How can I achieve this? I looked around a lot but couldn't find a similar issue.

If it were a model/snippet, I could have used property with ReadOnlyPanel but in the case of a StructBlock, I am stuck and not getting any ideas. If anybody knows any way to achieve this, please help me out. Thanks in advance.

ANSWER

Answered 2022-Mar-04 at 10:42

This is going to be quite difficult, since in current Wagtail versions (>=2.13) the StreamField editing UI is populated client-side in Javascript - as such, there are some extra steps to make the necessary data available to the client-side code, and the standard Django form / template mechanisms won't be available.

A simple workaround, if the fields aren't too long, would be to define the snippet's __str__ method to include the desired data - this will then display any time the snippet is shown in the admin.

For a full solution, I'd suggest this approach:

  • Set up a custom form_template for your StructBlock as detailed in Custom editing interfaces for StructBlock, with placeholder <div> elements to contain your additional data.

  • Override the StructBlock's get_form_state so that the returned dict (which will be used to populate the form template on the client side) includes your additional fields - something like:

1class SampleBlock(blocks.StructBlock):
2    service_snippet = SnippetChooserBlock(SampleSnippet)
3    ...
4
5def get_form_state(self, value):
6    data = super().get_form_state(value)
7    data['field_a'] = value['service_snippet'].field_a
8    data['field_b'] = value['service_snippet'].field_b
9    return data
10
  • Set up a custom JS adapter following the steps in Additional JavaScript on StructBlock forms. In the final render method, the data you returned from get_form_state will be available as initialState, and you can use this to populate the placeholder <div>s in the template.

  • Source https://stackoverflow.com/questions/71347329

    QUESTION

    How to switch wagtail homepage depending on user logged in status

    Asked 2022-Mar-03 at 09:36

    I have previously been using path("", include(wagtail_urls)) for my home_page url which displays the template at home/home_page.html correctly

    I wish to however display different pages depending on whether a user is logged in or not so have changed this to:

    1def logged_in_switch_view(logged_in_view, logged_out_view):
    2    '''switches views dependedn on logon status'''
    3    def inner_view(request, *args, **kwargs):
    4        if request.user.is_authenticated:
    5            return logged_in_view(request, *args, **kwargs)
    6        return logged_out_view(request, *args, **kwargs)
    7
    8    return inner_view 
    9
    10urlpatterns = [
    11
    12    path(&quot;&quot;, 
    13            logged_in_switch_view(
    14            TemplateView.as_view(template_name=&quot;home/home_page.html&quot;)),
    15            TemplateView.as_view(template_name=&quot;home/not_authenticated.html&quot;)),
    16            name=&quot;home&quot;),
    17]
    18

    With this approach (directly specifying the template rather than using wagtail_urls) the home page does not display correctly when logged in, in that all the wagtail tags in the html e.g. the blog posts are not displaying

    home_page.html

    1def logged_in_switch_view(logged_in_view, logged_out_view):
    2    '''switches views dependedn on logon status'''
    3    def inner_view(request, *args, **kwargs):
    4        if request.user.is_authenticated:
    5            return logged_in_view(request, *args, **kwargs)
    6        return logged_out_view(request, *args, **kwargs)
    7
    8    return inner_view 
    9
    10urlpatterns = [
    11
    12    path(&quot;&quot;, 
    13            logged_in_switch_view(
    14            TemplateView.as_view(template_name=&quot;home/home_page.html&quot;)),
    15            TemplateView.as_view(template_name=&quot;home/not_authenticated.html&quot;)),
    16            name=&quot;home&quot;),
    17]
    18{% extends &quot;base.html&quot; %}
    19{% load wagtailcore_tags wagtailimages_tags %}
    20{% block content %}
    21&lt;main class=&quot;container&quot;&gt;
    22    {% for post in page.blogs %}
    23    {% with post=post.specific %}
    24
    25    &lt;div class=&quot;col-md-8 mx-auto px-auto&quot;&gt;
    26        &lt;div class=&quot;row border rounded overflow-auto flex-md-row mb-4 shadow-sm position-relative &quot;&gt;
    27            &lt;div class=&quot;col p-4 d-flex flex-column position-static&quot;&gt;
    28                &lt;strong class=&quot;d-inline-block mb-2 text-primary&quot;&gt;{{ post.category }}&lt;/strong&gt;
    29                &lt;div class=&quot;mb-1 text-muted&quot;&gt;{{ post.date }}&lt;/div&gt;
    30                &lt;h3 class=&quot;mb-0&quot;&gt;{{ post.title }}&lt;/h3&gt;
    31                &lt;p&gt;{{post.intro}}&lt;/p&gt;
    32            &lt;/div&gt;
    33            
    34            &lt;div class=&quot;col-auto my-auto py-2 px-2 d-none d-lg-block&quot;&gt;
    35                &lt;a href=&quot;{% pageurl post %}&quot; class=&quot;stretched-link&quot;&gt;
    36                {% with post.main_image as main_image %}{% if main_image %}
    37                {% image main_image min-250x250  max-350x350%}
    38                {% endif %}{% endwith %}
    39                &lt;/a&gt;
    40            &lt;/div&gt;
    41        &lt;/div&gt;
    42        &lt;/div&gt;
    43    {% endwith %}
    44    {% endfor %}
    45        
    46    &lt;/main&gt;
    47
    48{% endblock %}
    49

    How should I specify the home_page in the logged_in_switch_view function?

    ANSWER

    Answered 2022-Mar-03 at 09:36

    The include(wagtail_urls) pulls in Wagtail's page handling logic for selecting which page should be returned for a given URL. If you swap that line out with your own code, you're effectively swapping out all of Wagtail...

    First, consider whether you're reinventing the page privacy feature that Wagtail already provides. If you want the site as a whole to require a login, and non-logged-in users to be given a blanket generic login form, you can enable this using the Privacy control under the Settings tab (or in the top right corner on older Wagtail releases) when editing the homepage, and set WAGTAIL_FRONTEND_LOGIN_TEMPLATE = "home/not_authenticated.html" in your project settings file.

    If you just want to swap the template for the homepage specifically, you can do that by defining a get_template method on your HomePage model:

    1def logged_in_switch_view(logged_in_view, logged_out_view):
    2    '''switches views dependedn on logon status'''
    3    def inner_view(request, *args, **kwargs):
    4        if request.user.is_authenticated:
    5            return logged_in_view(request, *args, **kwargs)
    6        return logged_out_view(request, *args, **kwargs)
    7
    8    return inner_view 
    9
    10urlpatterns = [
    11
    12    path(&quot;&quot;, 
    13            logged_in_switch_view(
    14            TemplateView.as_view(template_name=&quot;home/home_page.html&quot;)),
    15            TemplateView.as_view(template_name=&quot;home/not_authenticated.html&quot;)),
    16            name=&quot;home&quot;),
    17]
    18{% extends &quot;base.html&quot; %}
    19{% load wagtailcore_tags wagtailimages_tags %}
    20{% block content %}
    21&lt;main class=&quot;container&quot;&gt;
    22    {% for post in page.blogs %}
    23    {% with post=post.specific %}
    24
    25    &lt;div class=&quot;col-md-8 mx-auto px-auto&quot;&gt;
    26        &lt;div class=&quot;row border rounded overflow-auto flex-md-row mb-4 shadow-sm position-relative &quot;&gt;
    27            &lt;div class=&quot;col p-4 d-flex flex-column position-static&quot;&gt;
    28                &lt;strong class=&quot;d-inline-block mb-2 text-primary&quot;&gt;{{ post.category }}&lt;/strong&gt;
    29                &lt;div class=&quot;mb-1 text-muted&quot;&gt;{{ post.date }}&lt;/div&gt;
    30                &lt;h3 class=&quot;mb-0&quot;&gt;{{ post.title }}&lt;/h3&gt;
    31                &lt;p&gt;{{post.intro}}&lt;/p&gt;
    32            &lt;/div&gt;
    33            
    34            &lt;div class=&quot;col-auto my-auto py-2 px-2 d-none d-lg-block&quot;&gt;
    35                &lt;a href=&quot;{% pageurl post %}&quot; class=&quot;stretched-link&quot;&gt;
    36                {% with post.main_image as main_image %}{% if main_image %}
    37                {% image main_image min-250x250  max-350x350%}
    38                {% endif %}{% endwith %}
    39                &lt;/a&gt;
    40            &lt;/div&gt;
    41        &lt;/div&gt;
    42        &lt;/div&gt;
    43    {% endwith %}
    44    {% endfor %}
    45        
    46    &lt;/main&gt;
    47
    48{% endblock %}
    49class HomePage(Page):
    50    # ...
    51    def get_template(self, request, *args, **kwargs):
    52        if request.user.is_authenticated:
    53            return &quot;home/home_page.html&quot;
    54        else:
    55            return &quot;home/not_authenticated.html&quot;
    56

    This way, your homepage will select one template or the other, but still keep the page object available on the template.

    Source https://stackoverflow.com/questions/71332514

    QUESTION

    Adding external javascript snippet to a Wagtail page field

    Asked 2022-Feb-28 at 22:44

    My current Wagtail project has a very simple structure with some generic pages that have just a body RichTextField. These are just basic CMS type pages where an editor will edit some content in the rich text editor and publish.

    One of the pages (and probably more in time) is a "Help Wanted" page and uses an external javascript snippet from a third-party service that lists current job openings with links to the applications that are hosted by them.

    Since adding a <script> tag to the rich text editor simply escapes it and displays only the text value, I added an additional extra_scripts = models.TextField() to my page model along with a corresponding FieldPanel('extra_scripts') to the content panels. This lets a user paste in some arbitrary javascript snippets.

    If I include this field value in my template with {{page.extra_scripts}} it just displays the content and does not execute the javascript. Looking through the available wagtailcore_tags I'm not seeing a filter that I should use to execute the content, and when it's used with a {{}} tag, I presume the template engine handles it so that it doesn't execute.

    How can I include this field in a template so that the javascript is executed?

    I know I can change the field to just be the actual javascript content without the <script> tag, but the snippet also includes some basic HTML elements, and the end users aren't going to intuitively know that they need to manually edit the provided snippet if I only give them a field to paste certain parts of the snippet. I want the end user to be able to just copy/paste the snippet they get and have it work without them needing to understand anything else.

    ANSWER

    Answered 2022-Feb-28 at 22:44

    This is a result of the Django template language's auto-escaping behaviour, and can be overridden by adding a |safe filter:

    1{{ page.extra_scripts|safe }}
    2

    Of course, you should only do this if your editorial users are fully trusted, since it will give them the ability to run arbitrary code (including, for example, hijacking the session cookie of a superuser who happens to visit that page).

    Source https://stackoverflow.com/questions/71301680

    QUESTION

    Changing Template Location in Wagtail

    Asked 2022-Jan-29 at 18:23

    I'm trying to direct wagtail to use a template in a centralised location at the top of the project tree. So for example:

    1Project
    2|_ HomePage
    3|_ Search
    4|_ Mypage
    5|_ templates
    6

    My project re-uses templates and I'd like to give UX/UI Developers access to a single folder rather than multiple sub-folders in multiple pages.

    I've tried in Mypage/models.py

    1Project
    2|_ HomePage
    3|_ Search
    4|_ Mypage
    5|_ templates
    6class AdvisorSummaryPages(Page):
    7    
    8    intro = models.CharField(max_length=250, blank=True, null=True)
    9    
    10    content_panels = Page.content_panels + [
    11     FieldPanel('intro'),
    12     InlinePanel('carousell', heading=&quot;Carousell Images&quot;)   
    13    ]
    14    
    15    template = &quot;advisors.html&quot;
    16

    With the following template setting in Project/Project/settings/base.py

    1Project
    2|_ HomePage
    3|_ Search
    4|_ Mypage
    5|_ templates
    6class AdvisorSummaryPages(Page):
    7    
    8    intro = models.CharField(max_length=250, blank=True, null=True)
    9    
    10    content_panels = Page.content_panels + [
    11     FieldPanel('intro'),
    12     InlinePanel('carousell', heading=&quot;Carousell Images&quot;)   
    13    ]
    14    
    15    template = &quot;advisors.html&quot;
    16TEMPLATES = [
    17    {
    18        'BACKEND': 'django.template.backends.django.DjangoTemplates',
    19        'DIRS': [
    20            os.path.join(BASE_DIR, 'templates'),
    21        ],
    22

    With no luck. I can't seem to find any solution on SO or through the documentation or Google that might work. There is a solution presented here using separate model admins but that doesn't work for me. How might I specify the location of the template differently to a subdirectory of templates in the MyPage App?

    Thanks

    ANSWER

    Answered 2022-Jan-29 at 18:23

    Below is how I organize templates and static assets. I have a themes folder that is located in the main project folder with named theme subfolders within the themes folder. I then have the following in settings/base.py:

    1Project
    2|_ HomePage
    3|_ Search
    4|_ Mypage
    5|_ templates
    6class AdvisorSummaryPages(Page):
    7    
    8    intro = models.CharField(max_length=250, blank=True, null=True)
    9    
    10    content_panels = Page.content_panels + [
    11     FieldPanel('intro'),
    12     InlinePanel('carousell', heading=&quot;Carousell Images&quot;)   
    13    ]
    14    
    15    template = &quot;advisors.html&quot;
    16TEMPLATES = [
    17    {
    18        'BACKEND': 'django.template.backends.django.DjangoTemplates',
    19        'DIRS': [
    20            os.path.join(BASE_DIR, 'templates'),
    21        ],
    22PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    23BASE_DIR = os.path.dirname(PROJECT_DIR)
    24
    25THEMES_URL = '/themes/'
    26THEME_PATH = os.path.join(BASE_DIR, THEMES_URL.replace('/', ''), 'name_of_theme')
    27
    28TEMPLATES = [
    29    {
    30        'BACKEND': 'django.template.backends.django.DjangoTemplates',
    31        'DIRS': [
    32            THEME_PATH,
    33            ...other directories...,
    34        ],
    35... other TEMPLATES-RELATED CODE
    36     }
    37]
    38
    39STATICFILES_DIRS = [
    40    os.path.join(THEME_PATH, 'static'),
    41]
    42
    43
    44STATIC_ROOT = os.path.join(BASE_DIR, 'static')
    45STATIC_URL = '/static/'
    46

    The name_of_theme folder in the THEME_PATH definition contains all of the templates and static files for the theme. In order for the static files to be collected correctly, the folder structure for the css, js, etc. files within each theme folder needs to be: /themes/name_of_theme/static/name_of_theme/js (or css, etc.)/filename.js (or filename.css, etc.)

    The /name_of_theme/static/name_of_theme/ namespacing is necessary for collectstatic to be able to collect the files correctly (see Staticfile namespacing here for more info). When including a reference to a static file in a template, you then do:

    {% static 'name_of_theme/js/filename.js' %}

    The STATICFILES_DIRS definition is only set up for one theme. You would need to change or add to that if you're using more than one theme.

    Some time ago I also came across this Wagtail package: Wagtail Themes. It looks very interesting, but I'm not sure if it provides for handling static files organized within named theme folders as I describe above.

    Source https://stackoverflow.com/questions/70774070

    QUESTION

    Wagtail 'View live' button provides wrong url after page creation while using id as slug

    Asked 2022-Jan-26 at 14:39

    I have a case that uses Page ID as a slug, after creating a new Page, Wagtail provides us a "View live" button but when we click on that button, it provides a wrong URL

    enter image description here

    enter image description here

    The right URL should be ".../property-list/<property-id>"

    I have searched on stack overflow, found this thread but the answer is still a mystery: Wrong 'View live' url in Wagtail admin message after page creation when using id as slug

    I have followed the Wagtail official document, using Wagtail Hooks to manipulate data. However, no success yet. This is my code:

    1@hooks.register('after_create_page')
    2def set_number_and_slug_after_property_page_created(request, page):
    3    page.number = page.slug = str(page.id)
    4    page.save()
    5    new_revision = page.save_revision()
    6    if page.live:
    7        new_revision.publish()
    8

    Please help me, thank you.

    ANSWER

    Answered 2022-Jan-26 at 14:39

    The after_create_page hook doesn't work for this, as it's one of the last things to run during the Wagtail admin's page creation view, and the confirmation message (including the old URL) has already been constructed by that point. This can be remedied by using the post_save signal provided by Django instead - being a more low-level mechanism, it's more closely tied to the act of saving the database record, without Wagtail's admin logic getting in the way.

    (It's also a bit more future-proof: Wagtail's after_create_page hook is designed to only be called when a user goes through the page creation area of the Wagtail admin, so that it can be used to customise that user's path through the admin if appropriate. If there's ever any other route by which a page might be created - like, say, a data import, or using translation tools for a multi-language site - then after_create_page will be bypassed, but the post_save signal will still be triggered.)

    Assuming your project has a properties app where you're defining a PropertyPage model, your code can be rewritten to use post_save as follows - in properties/signals.py:

    1@hooks.register('after_create_page')
    2def set_number_and_slug_after_property_page_created(request, page):
    3    page.number = page.slug = str(page.id)
    4    page.save()
    5    new_revision = page.save_revision()
    6    if page.live:
    7        new_revision.publish()
    8from django.db.models.signals import post_save
    9from django.dispatch import receiver
    10
    11from .models import PropertyPage
    12
    13
    14@receiver(post_save)
    15def set_number_and_slug_after_property_page_created(sender, instance, created, **kwargs):
    16    if issubclass(sender, PropertyPage) and created:
    17        page = instance
    18        page.number = page.slug = str(page.id)
    19        page.save()
    20        new_revision = page.save_revision()
    21        if page.live:
    22            new_revision.publish()
    23

    Then, to connect this signal up on server startup, create a properties/apps.py containing the following (or just add / edit the ready method if you have an AppConfig class there already):

    1@hooks.register('after_create_page')
    2def set_number_and_slug_after_property_page_created(request, page):
    3    page.number = page.slug = str(page.id)
    4    page.save()
    5    new_revision = page.save_revision()
    6    if page.live:
    7        new_revision.publish()
    8from django.db.models.signals import post_save
    9from django.dispatch import receiver
    10
    11from .models import PropertyPage
    12
    13
    14@receiver(post_save)
    15def set_number_and_slug_after_property_page_created(sender, instance, created, **kwargs):
    16    if issubclass(sender, PropertyPage) and created:
    17        page = instance
    18        page.number = page.slug = str(page.id)
    19        page.save()
    20        new_revision = page.save_revision()
    21        if page.live:
    22            new_revision.publish()
    23from django.apps import AppConfig
    24
    25
    26class PropertiesConfig(AppConfig):
    27    name = 'properties'
    28
    29    def ready(self):
    30        from . import signals
    31

    Source https://stackoverflow.com/questions/70515946

    QUESTION

    Testing a Wagtail StructBlock with a StreamBlockField

    Asked 2021-Dec-29 at 15:02

    I'm attempting to run some unit tests on a StructBlock, which is composed of normal block fields and a StreamBlock.

    The problem I'm running into is that I can construct a test that renders the block, but I cannot test the StreamBlock validation (i.e., I can't test the block's clean())

    Stack

    • Python 3.9.6
    • Django 3.2
    • Wagtail 2.13
    • pytest 6.2
    • pytest-django 4.4

    Block Definitions

    MyStructBlock

    1    class MyStructBlock(blocks.StructBlock):
    2        image = ImageChooserBlock()
    3
    4        heading = blocks.CharBlock(required=True)
    5        description = blocks.RichTextBlock(features=[&quot;bold&quot;, &quot;italic&quot;])
    6        links = blocks.StreamBlock([(&quot;link&quot;, LinkBlock())], icon=&quot;link&quot;, min_num=1, max_num=6)
    7
    8        class Meta:
    9            icon = &quot;list-ul&quot;
    10            template = &quot;blocks/two_column_with_link_list_block.html&quot;
    11

    LinkBlock

    1    class MyStructBlock(blocks.StructBlock):
    2        image = ImageChooserBlock()
    3
    4        heading = blocks.CharBlock(required=True)
    5        description = blocks.RichTextBlock(features=[&quot;bold&quot;, &quot;italic&quot;])
    6        links = blocks.StreamBlock([(&quot;link&quot;, LinkBlock())], icon=&quot;link&quot;, min_num=1, max_num=6)
    7
    8        class Meta:
    9            icon = &quot;list-ul&quot;
    10            template = &quot;blocks/two_column_with_link_list_block.html&quot;
    11    class LinkBlock(blocks.StructBlock):
    12        link_text = blocks.CharBlock()
    13        internal_link = blocks.PageChooserBlock()
    14        internal_document = DocumentChooserBlock()
    15        external_link = blocks.URLBlock()
    16

    With this setup I create this kind of block_definition:

    1    class MyStructBlock(blocks.StructBlock):
    2        image = ImageChooserBlock()
    3
    4        heading = blocks.CharBlock(required=True)
    5        description = blocks.RichTextBlock(features=[&quot;bold&quot;, &quot;italic&quot;])
    6        links = blocks.StreamBlock([(&quot;link&quot;, LinkBlock())], icon=&quot;link&quot;, min_num=1, max_num=6)
    7
    8        class Meta:
    9            icon = &quot;list-ul&quot;
    10            template = &quot;blocks/two_column_with_link_list_block.html&quot;
    11    class LinkBlock(blocks.StructBlock):
    12        link_text = blocks.CharBlock()
    13        internal_link = blocks.PageChooserBlock()
    14        internal_document = DocumentChooserBlock()
    15        external_link = blocks.URLBlock()
    16{
    17   'image': &lt;Image: Thumbnail of subject&gt;, 
    18   'heading': 'SomeText', 
    19   'description': 'Hello, Son', 
    20   'links':  
    21     [
    22       {'value': 
    23         {
    24          'link_text': 'Walk trip thought region.', 
    25          'internal_link': None, 
    26          'document_link': None, 
    27          'external_link': 'www.example.com'
    28         }
    29       },
    30       {'value': {...}},
    31       {'value': {...}},
    32      ]
    33}
    34

    Then a test like this will work just fine:

    1    class MyStructBlock(blocks.StructBlock):
    2        image = ImageChooserBlock()
    3
    4        heading = blocks.CharBlock(required=True)
    5        description = blocks.RichTextBlock(features=[&quot;bold&quot;, &quot;italic&quot;])
    6        links = blocks.StreamBlock([(&quot;link&quot;, LinkBlock())], icon=&quot;link&quot;, min_num=1, max_num=6)
    7
    8        class Meta:
    9            icon = &quot;list-ul&quot;
    10            template = &quot;blocks/two_column_with_link_list_block.html&quot;
    11    class LinkBlock(blocks.StructBlock):
    12        link_text = blocks.CharBlock()
    13        internal_link = blocks.PageChooserBlock()
    14        internal_document = DocumentChooserBlock()
    15        external_link = blocks.URLBlock()
    16{
    17   'image': &lt;Image: Thumbnail of subject&gt;, 
    18   'heading': 'SomeText', 
    19   'description': 'Hello, Son', 
    20   'links':  
    21     [
    22       {'value': 
    23         {
    24          'link_text': 'Walk trip thought region.', 
    25          'internal_link': None, 
    26          'document_link': None, 
    27          'external_link': 'www.example.com'
    28         }
    29       },
    30       {'value': {...}},
    31       {'value': {...}},
    32      ]
    33}
    34   def test_structure():
    35      my_struct_block = MyStructBlock()
    36      block_def = block_definition
    37      rendered_block_html = BeautifulSoup(my_struct_block.render(value=block_def)))
    38      assert &lt;&lt;THINGS ABOUT THE STRUCTURE&gt;&gt;
    39

    The block renders and all is well, but when I try to clean the block everything starts to go sideways.

    1    class MyStructBlock(blocks.StructBlock):
    2        image = ImageChooserBlock()
    3
    4        heading = blocks.CharBlock(required=True)
    5        description = blocks.RichTextBlock(features=[&quot;bold&quot;, &quot;italic&quot;])
    6        links = blocks.StreamBlock([(&quot;link&quot;, LinkBlock())], icon=&quot;link&quot;, min_num=1, max_num=6)
    7
    8        class Meta:
    9            icon = &quot;list-ul&quot;
    10            template = &quot;blocks/two_column_with_link_list_block.html&quot;
    11    class LinkBlock(blocks.StructBlock):
    12        link_text = blocks.CharBlock()
    13        internal_link = blocks.PageChooserBlock()
    14        internal_document = DocumentChooserBlock()
    15        external_link = blocks.URLBlock()
    16{
    17   'image': &lt;Image: Thumbnail of subject&gt;, 
    18   'heading': 'SomeText', 
    19   'description': 'Hello, Son', 
    20   'links':  
    21     [
    22       {'value': 
    23         {
    24          'link_text': 'Walk trip thought region.', 
    25          'internal_link': None, 
    26          'document_link': None, 
    27          'external_link': 'www.example.com'
    28         }
    29       },
    30       {'value': {...}},
    31       {'value': {...}},
    32      ]
    33}
    34   def test_structure():
    35      my_struct_block = MyStructBlock()
    36      block_def = block_definition
    37      rendered_block_html = BeautifulSoup(my_struct_block.render(value=block_def)))
    38      assert &lt;&lt;THINGS ABOUT THE STRUCTURE&gt;&gt;
    39   def test_validation():
    40       my_struct_block = MyStructBlock()
    41       block_def = block_definition
    42       rendered_block_html = BeautifulSoup(my_struct_block.render(my_struct_block.clean(block_def)))
    43
    44

    Will result in something like this:

    1    class MyStructBlock(blocks.StructBlock):
    2        image = ImageChooserBlock()
    3
    4        heading = blocks.CharBlock(required=True)
    5        description = blocks.RichTextBlock(features=[&quot;bold&quot;, &quot;italic&quot;])
    6        links = blocks.StreamBlock([(&quot;link&quot;, LinkBlock())], icon=&quot;link&quot;, min_num=1, max_num=6)
    7
    8        class Meta:
    9            icon = &quot;list-ul&quot;
    10            template = &quot;blocks/two_column_with_link_list_block.html&quot;
    11    class LinkBlock(blocks.StructBlock):
    12        link_text = blocks.CharBlock()
    13        internal_link = blocks.PageChooserBlock()
    14        internal_document = DocumentChooserBlock()
    15        external_link = blocks.URLBlock()
    16{
    17   'image': &lt;Image: Thumbnail of subject&gt;, 
    18   'heading': 'SomeText', 
    19   'description': 'Hello, Son', 
    20   'links':  
    21     [
    22       {'value': 
    23         {
    24          'link_text': 'Walk trip thought region.', 
    25          'internal_link': None, 
    26          'document_link': None, 
    27          'external_link': 'www.example.com'
    28         }
    29       },
    30       {'value': {...}},
    31       {'value': {...}},
    32      ]
    33}
    34   def test_structure():
    35      my_struct_block = MyStructBlock()
    36      block_def = block_definition
    37      rendered_block_html = BeautifulSoup(my_struct_block.render(value=block_def)))
    38      assert &lt;&lt;THINGS ABOUT THE STRUCTURE&gt;&gt;
    39   def test_validation():
    40       my_struct_block = MyStructBlock()
    41       block_def = block_definition
    42       rendered_block_html = BeautifulSoup(my_struct_block.render(my_struct_block.clean(block_def)))
    43
    44self = &lt;wagtail.core.blocks.field_block.RichTextBlock object at 0x7f874f430580&gt;, value = 'Hello, Son'
    45
    46    def value_for_form(self, value):
    47        # Rich text editors take the source-HTML string as input (and takes care
    48        # of expanding it for the purposes of the editor)
    49&gt;       return value.source
    50E       AttributeError: 'str' object has no attribute 'source'
    51
    52.direnv/python-3.9.6/lib/python3.9/site-packages/wagtail/core/blocks/field_block.py:602: AttributeError
    53

    Which is descriptive enough -- I get that it requires a Richtext Block definition -- but why the difference?

    NOTE: I have tried defining the block_definition using RichText as the value for fields that expect RichText, this fails. If I remove the RichText field, the clean proceeds to fail on the dicts I used to define the LinkBlock.

    Question

    Is there a canonical way to set up a test like this that can handle complex blocks, so that a common source of block definition can test render as well as clean and render? Or, does this type of block, in some way, require an integration approach where I construct a Page with the complex blocks added as stream_data and then request the Page and use the response's rendering?

    ANSWER

    Answered 2021-Dec-29 at 15:02

    Every block type has a corresponding 'native' data type for the data it expects to work with - for the simpler blocks, this data type is what you'd expect (e.g. a string for CharBlock, an Image instance for ImageChooserBlock) but for a few of the more complex ones, there's a custom type defined:

    • for RichTextBlock, the native type is wagtail.core.rich_text.RichText (which behaves similarly to a string, but also has a source property where e.g. page IDs in page links are kept intact)
    • for StreamBlock, the native type is wagtail.core.blocks.StreamValue (a sequence type, where each item is a StreamValue with block_type and value properties).

    The render method will generally be quite forgiving if you use the wrong types (such as a string for RichTextBlock or a list of dicts for StreamBlock), since it's really just invoking your own template code. The clean method will be more picky, since it's running Python logic specific to each block.

    Unfortunately the correct types to use for each block aren't really formally documented, and some of them are quite fiddly to construct (e.g. a StructValue needs to be passed a reference to the corresponding StructBlock) - outside of test code, there isn't much need to create these objects from scratch, because the data will usually be coming from some outside source instead (e.g. a form submission or the database), and each block will be responsible for converting that to its native type.

    With that in mind, I'd recommend that you construct your data by piggybacking on the to_python method, which converts the JSON representation as stored in the database (consisting of just simple Python data types - integers, strings, lists, dicts) into the native data types:

    1    class MyStructBlock(blocks.StructBlock):
    2        image = ImageChooserBlock()
    3
    4        heading = blocks.CharBlock(required=True)
    5        description = blocks.RichTextBlock(features=[&quot;bold&quot;, &quot;italic&quot;])
    6        links = blocks.StreamBlock([(&quot;link&quot;, LinkBlock())], icon=&quot;link&quot;, min_num=1, max_num=6)
    7
    8        class Meta:
    9            icon = &quot;list-ul&quot;
    10            template = &quot;blocks/two_column_with_link_list_block.html&quot;
    11    class LinkBlock(blocks.StructBlock):
    12        link_text = blocks.CharBlock()
    13        internal_link = blocks.PageChooserBlock()
    14        internal_document = DocumentChooserBlock()
    15        external_link = blocks.URLBlock()
    16{
    17   'image': &lt;Image: Thumbnail of subject&gt;, 
    18   'heading': 'SomeText', 
    19   'description': 'Hello, Son', 
    20   'links':  
    21     [
    22       {'value': 
    23         {
    24          'link_text': 'Walk trip thought region.', 
    25          'internal_link': None, 
    26          'document_link': None, 
    27          'external_link': 'www.example.com'
    28         }
    29       },
    30       {'value': {...}},
    31       {'value': {...}},
    32      ]
    33}
    34   def test_structure():
    35      my_struct_block = MyStructBlock()
    36      block_def = block_definition
    37      rendered_block_html = BeautifulSoup(my_struct_block.render(value=block_def)))
    38      assert &lt;&lt;THINGS ABOUT THE STRUCTURE&gt;&gt;
    39   def test_validation():
    40       my_struct_block = MyStructBlock()
    41       block_def = block_definition
    42       rendered_block_html = BeautifulSoup(my_struct_block.render(my_struct_block.clean(block_def)))
    43
    44self = &lt;wagtail.core.blocks.field_block.RichTextBlock object at 0x7f874f430580&gt;, value = 'Hello, Son'
    45
    46    def value_for_form(self, value):
    47        # Rich text editors take the source-HTML string as input (and takes care
    48        # of expanding it for the purposes of the editor)
    49&gt;       return value.source
    50E       AttributeError: 'str' object has no attribute 'source'
    51
    52.direnv/python-3.9.6/lib/python3.9/site-packages/wagtail/core/blocks/field_block.py:602: AttributeError
    53block_def = my_struct_block.to_python({
    54   'image': 123,  # the image ID
    55   'heading': 'SomeText', 
    56   'description': 'Hello, Son', 
    57   'links':  
    58     [
    59       {'type': 'link', 'value': 
    60         {
    61          'link_text': 'Walk trip thought region.', 
    62          'internal_link': None, 
    63          'document_link': None, 
    64          'external_link': 'www.example.com'
    65         }
    66       },
    67       {'type': 'link', 'value': {...}},
    68       {'type': 'link', 'value': {...}},
    69      ]
    70})
    71

    my_struct_block.clean(block_def) will hopefully then succeed.

    Source https://stackoverflow.com/questions/70520041

    QUESTION

    How to customize the admin form for a custom image model in Wagtail CMS?

    Asked 2021-Dec-02 at 16:45

    I need to add a string based unique ID to Wagtail’s image model. These IDs are a relatively short combination of letters, numbers and punctuation, e.g. "AS.M-1.001". So I am using a custom image model with Django’s standard CharField for that with the argument unique=True. But the editing form unfortunately does not check if the ID is unique. So I can use the same ID for multiple images. This check for uniqueness does work in any other standard form in Wagtail, e.g. the Page model. But not for the image model.

    1from django.db import models
    2from wagtail.images.models import Image, AbstractImage
    3
    4class CustomImage(AbstractImage):
    5    custom_id = models.CharField(max_length=32, unique=True, null=True, blank=True)
    6    admin_form_fields =  ( 'custom_id', ) + Image.admin_form_fields
    7

    My approach would be to override the editing form with a custom one to display more warnings and errors, like you can do with base_form_class for Wagtail’s Page model etc., as documented here. I tried both wagtail.admin.forms.WagtailAdminModelForm as well as wagtail.images.forms.BaseImageForm.

    1from django.db import models
    2from wagtail.images.models import Image, AbstractImage
    3
    4class CustomImage(AbstractImage):
    5    custom_id = models.CharField(max_length=32, unique=True, null=True, blank=True)
    6    admin_form_fields =  ( 'custom_id', ) + Image.admin_form_fields
    7from wagtail.images.forms import BaseImageForm
    8from wagtail.admin.forms import WagtailAdminModelForm
    9
    10class CustomImageForm(WagtailAdminModelForm):
    11    # add more logic here
    12    pass
    13
    14class CustomImage(ArtButlerIDMixin, AbstractImage):
    15    ...
    16    base_form_class = CustomImageForm
    17

    Both lead to the same exception:

    1from django.db import models
    2from wagtail.images.models import Image, AbstractImage
    3
    4class CustomImage(AbstractImage):
    5    custom_id = models.CharField(max_length=32, unique=True, null=True, blank=True)
    6    admin_form_fields =  ( 'custom_id', ) + Image.admin_form_fields
    7from wagtail.images.forms import BaseImageForm
    8from wagtail.admin.forms import WagtailAdminModelForm
    9
    10class CustomImageForm(WagtailAdminModelForm):
    11    # add more logic here
    12    pass
    13
    14class CustomImage(ArtButlerIDMixin, AbstractImage):
    15    ...
    16    base_form_class = CustomImageForm
    17raise AppRegistryNotReady(&quot;Models aren't loaded yet.&quot;)
    18

    So a tried to resort the apps in my settings with no effect. Does the standard approach how to override an admin form in Wagtail work for the image model at all? What could be other ways to get a unique string identifier working here? ... or to customize this form.

    Solution (Update)

    Following @gasman’s advice, I added the following line to my settings/base.py:

    1from django.db import models
    2from wagtail.images.models import Image, AbstractImage
    3
    4class CustomImage(AbstractImage):
    5    custom_id = models.CharField(max_length=32, unique=True, null=True, blank=True)
    6    admin_form_fields =  ( 'custom_id', ) + Image.admin_form_fields
    7from wagtail.images.forms import BaseImageForm
    8from wagtail.admin.forms import WagtailAdminModelForm
    9
    10class CustomImageForm(WagtailAdminModelForm):
    11    # add more logic here
    12    pass
    13
    14class CustomImage(ArtButlerIDMixin, AbstractImage):
    15    ...
    16    base_form_class = CustomImageForm
    17raise AppRegistryNotReady(&quot;Models aren't loaded yet.&quot;)
    18WAGTAILIMAGES_IMAGE_MODEL = 'images.CustomImage'
    19WAGTAILIMAGES_IMAGE_FORM_BASE = 'images.forms.CustomImageForm'  # NEW
    20

    And added a the following form to a forms.py in my images app:

    1from django.db import models
    2from wagtail.images.models import Image, AbstractImage
    3
    4class CustomImage(AbstractImage):
    5    custom_id = models.CharField(max_length=32, unique=True, null=True, blank=True)
    6    admin_form_fields =  ( 'custom_id', ) + Image.admin_form_fields
    7from wagtail.images.forms import BaseImageForm
    8from wagtail.admin.forms import WagtailAdminModelForm
    9
    10class CustomImageForm(WagtailAdminModelForm):
    11    # add more logic here
    12    pass
    13
    14class CustomImage(ArtButlerIDMixin, AbstractImage):
    15    ...
    16    base_form_class = CustomImageForm
    17raise AppRegistryNotReady(&quot;Models aren't loaded yet.&quot;)
    18WAGTAILIMAGES_IMAGE_MODEL = 'images.CustomImage'
    19WAGTAILIMAGES_IMAGE_FORM_BASE = 'images.forms.CustomImageForm'  # NEW
    20from django.core.exceptions import ValidationError
    21from wagtail.images.forms import BaseImageForm
    22from .models import CustomImage
    23
    24class CustomImageForm(BaseImageForm):
    25
    26    def clean(self):
    27        cleaned_data = super().clean()
    28        custom_id = cleaned_data.get(&quot;custom_id&quot;)
    29
    30        if CustomImage.objects.filter(custom_id=custom_id).exists():
    31            raise ValidationError(
    32                    &quot;Custom ID already exists&quot;
    33                )
    34
    35        return cleaned_data
    36

    ANSWER

    Answered 2021-Dec-02 at 14:36

    Images in Wagtail don't use WagtailAdminModelForm or the base_form_class attribute - these are used by pages, snippets and ModelAdmin to support Wagtail-specific features like inline children and panels, but images work through plain Django models and forms.

    You can customise the form by subclassing BaseImageForm and setting WAGTAILIMAGES_IMAGE_FORM_BASE in your project settings. As long as you define your form class somewhere outside of models.py (e.g. in a separate forms.py module), you'll avoid the circular dependency that leads to the "Models aren't loaded yet" error.

    Source https://stackoverflow.com/questions/70200660

    QUESTION

    Why won't a Wagtail TableBlock Display Properly in the Admin?

    Asked 2021-Nov-05 at 01:53

    I'd like to have a TableBlock display in my admin panel, but it isn't displaying properly.

    Here are the errors I'm getting: JavaScript Console

    And here's the code block:

    1from wagtail.contrib.table_block.blocks import TableBlock
    2from wagtail.core.blocks import StreamBlock
    3from wagtail.core.fields import StreamField
    4
    5class BaseStreamBlock(StreamBlock):
    6    table = TableBlock()
    7
    8
    9class ArticlePage(Page):
    10    parent_page_types = ['home.HomePage']
    11    subpage_types = []
    12
    13    content = StreamField(BaseStreamBlock(), verbose_name=_('Content'), blank=True)
    14
    15    content_panels = [
    16        MultiFieldPanel([
    17            FieldPanel('title'),
    18        ]),
    19        MultiFieldPanel(
    20            [
    21                StreamFieldPanel('content'),
    22            ]
    23        ),
    24    ]
    25

    enter image description here

    ANSWER

    Answered 2021-Nov-03 at 17:36

    The errors in the browser console show that the Javascript files included in the wagtail.contrib.table_block app are not loading. Most likely, these are missing from your S3 file hosting (S3 returns 403 Forbidden for missing files).

    After adding wagtail.contrib.table_block to INSTALLED_APPS and deploying to your server, you'll need to re-run ./manage.py collectstatic to ensure these JS files are uploaded to S3.

    Source https://stackoverflow.com/questions/69828781

    Community Discussions contain sources that include Stack Exchange Network

    Tutorials and Learning Resources in Wagtail

    Tutorials and Learning Resources are not available at this moment for Wagtail

    Share this Page

    share link

    Get latest updates on Wagtail