question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

wagtail v4 new page issue

See original GitHub issue

When trying to crate a new page in the example django app in the project the following error happens.

Seems to relate to the FormChooserBlock as when I comment out form = FormChooserBlock() (line #45 in blocks.py) the page renders.

Can’t seem to see what the issue could be. But this might be the only blocker for supporting version 4?

To reproduce, edit the setup.py to require wagtail==4.0.* docker-compose up then try to add a child page to the default page.

app_1  | Internal Server Error: /cms/pages/add/example/basicpage/2/
app_1  | Traceback (most recent call last):
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 55, in inner
app_1  |     response = get_response(request)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py", line 220, in _get_response
app_1  |     response = response.render()
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/response.py", line 114, in render
app_1  |     self.content = self.rendered_content
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/response.py", line 92, in rendered_content
app_1  |     return template.render(context, self._request)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/backends/django.py", line 62, in render
app_1  |     return self.template.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 175, in render
app_1  |     return self._render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 167, in _render
app_1  |     return self.nodelist.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
app_1  |     return self.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/loader_tags.py", line 157, in render
app_1  |     return compiled_parent._render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 167, in _render
app_1  |     return self.nodelist.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
app_1  |     return self.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/loader_tags.py", line 157, in render
app_1  |     return compiled_parent._render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 167, in _render
app_1  |     return self.nodelist.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
app_1  |     return self.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/loader_tags.py", line 157, in render
app_1  |     return compiled_parent._render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 167, in _render
app_1  |     return self.nodelist.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
app_1  |     return self.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/loader_tags.py", line 63, in render
app_1  |     result = block.nodelist.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
app_1  |     return self.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/loader_tags.py", line 63, in render
app_1  |     result = block.nodelist.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
app_1  |     return self.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1064, in render
app_1  |     output = self.filter_expression.resolve(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 715, in resolve
app_1  |     obj = self.var.resolve(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 847, in resolve
app_1  |     value = self._resolve_lookup(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 914, in _resolve_lookup
app_1  |     current = current()
app_1  |   File "/usr/local/lib/python3.8/site-packages/wagtail/admin/panels.py", line 392, in render_form_content
app_1  |     return mark_safe(self.render_html() + self.render_missing_fields())
app_1  |   File "/usr/local/lib/python3.8/site-packages/wagtail/admin/ui/components.py", line 20, in render_html
app_1  |     return template.render(context_data)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/backends/django.py", line 62, in render
app_1  |     return self.template.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 175, in render
app_1  |     return self._render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 167, in _render
app_1  |     return self.nodelist.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
app_1  |     return self.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/defaulttags.py", line 238, in render
app_1  |     nodelist.append(node.render_annotated(context))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
app_1  |     return self.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1064, in render
app_1  |     output = self.filter_expression.resolve(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 715, in resolve
app_1  |     obj = self.var.resolve(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 847, in resolve
app_1  |     value = self._resolve_lookup(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 914, in _resolve_lookup
app_1  |     current = current()
app_1  |   File "/usr/local/lib/python3.8/site-packages/wagtail/admin/ui/components.py", line 20, in render_html
app_1  |     return template.render(context_data)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/backends/django.py", line 62, in render
app_1  |     return self.template.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 175, in render
app_1  |     return self._render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 167, in _render
app_1  |     return self.nodelist.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
app_1  |     return self.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/defaulttags.py", line 238, in render
app_1  |     nodelist.append(node.render_annotated(context))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
app_1  |     return self.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/wagtail/admin/templatetags/wagtailadmin_tags.py", line 948, in render
app_1  |     children = self.nodelist.render(context) if self.nodelist else ""
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in render
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 1005, in <listcomp>
app_1  |     return SafeString("".join([node.render_annotated(context) for node in self]))
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/base.py", line 966, in render_annotated
app_1  |     return self.render(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/template/library.py", line 237, in render
app_1  |     output = self.func(*resolved_args, **resolved_kwargs)
app_1  |   File "/usr/local/lib/python3.8/site-packages/wagtail/admin/templatetags/wagtailadmin_tags.py", line 876, in component
app_1  |     return obj.render_html(context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/wagtail/admin/ui/components.py", line 15, in render_html
app_1  |     context_data = self.get_context_data(parent_context)
app_1  |   File "/usr/local/lib/python3.8/site-packages/wagtail/admin/panels.py", line 814, in get_context_data
app_1  |     rendered_field = self.bound_field.as_widget(attrs=widget_attrs)
app_1  |   File "/usr/local/lib/python3.8/site-packages/django/forms/boundfield.py", line 99, in as_widget
app_1  |     return widget.render(
app_1  |   File "/usr/local/lib/python3.8/site-packages/wagtail/blocks/base.py", line 552, in render
app_1  |     return self.render_with_errors(
app_1  |   File "/usr/local/lib/python3.8/site-packages/wagtail/blocks/base.py", line 546, in render_with_errors
app_1  |     block_json=self.block_json,
app_1  |   File "/usr/local/lib/python3.8/site-packages/wagtail/blocks/base.py", line 523, in block_json
app_1  |     return self._block_json
app_1  | AttributeError: 'BlockWidget' object has no attribute '_block_json'

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:8

github_iconTop GitHub Comments

1reaction
funkhaus-philcommented, Nov 17, 2022

The Problem here is, that wagtail reworked the BaseChooser Widget. Wagtailstreamforms created a FormChooser which inherits wagtails BaseChooser and overwrites the inner widget, which does not work anymore. I was not able to figure out how get the FormChooser to run again under the new circumstances but figured we could just use a ChoiceBlock instead.

So here are the good news, at least for a hotfix inside your own Project. In my case we use wagtail as a headless CMS and consume the API to represent the data in an external Frontend Application. In that case you can simply overwrite the FormChooser like so with a little helper function like so:

from wagtail import blocks
from wagtailstreamforms.blocks import WagtailFormBlock
from wagtailstreamforms.models.form import Form

def getFormblockChoices():
	choices = []
	forms = Form.objects.all()
	for form in forms:
		choices.append((form.id, form.title))
	return choices

class APIWagtailFormBlock(WagtailFormBlock):
	FORMBLOCK_CHOICES = getFormblockChoices()
	form = blocks.ChoiceBlock(choices=FORMBLOCK_CHOICES)

	def get_api_representation(self, value, context=None):
		if value:
			return YourFormSerializer(context=context).to_representation(value)
		else:
			return None

Keep in mind that a ChoiceBlock returns a string, which makes it neccessary to overwrite the serializer as well, since it is expecting a model instance and not a string representation of a model. My ChoiceBlock returns the ID of the Form to grab the Instance we normally expect with the Object Manager like this:

from wagtailstreamforms.models.form import Form

form = Form.objects.get(id=obj['form'])

I bet that you could simply write a simple template tag that does exactly that if you are using wagtail the standard way to render your content.

Hope this helps at least for a quickfix and possibly with fixing the problem long term. Greetings, Phil.

0reactions
TonisPiipcommented, Dec 20, 2022
from django.db import DatabaseError

from wagtail.blocks import ChoiceBlock
from wagtailstreamforms.blocks import WagtailFormBlock
from wagtailstreamforms.models.form import Form as StreamForm


class HackChoiceBlock(ChoiceBlock):
    "Dont make new migations depending on DB state..."

    def deconstruct(self):
        _constructor_kwargs = self._constructor_kwargs
        _constructor_kwargs["choices"] = []
        return ("wagtail.blocks.ChoiceBlock", [], self._constructor_kwargs)

    @staticmethod
    def get_choices():
        """
        Choices shouldn't access the db, or should be done lazily, however this is a hack in a hack
        Main issue is that this db access is causing linting and publish to crash as it can't access the db
        """
        try:
            return tuple(StreamForm.objects.values_list("id", "title"))
        except DatabaseError:
            return []


class FormBlock(WagtailFormBlock):
    """
    Wagatil v4 fix: https://github.com/labd/wagtailstreamforms/issues/200#issuecomment-1318933142"""

    form = HackChoiceBlock(choices=HackChoiceBlock.get_choices())

    def render(self, value, context=None):

        # Substitute form (containing only the id) with the actual form
        form = value["form"] = StreamForm.objects.filter(pk=value["form"]).first()

        # check if we have a form, as they can be deleted, and we dont want to break the site with
        # a none template value
        if form:
            self.meta.template = form.template_name
        else:
            self.meta.template = "streamforms/non_existent_form.html"
        return super().render(value, context)

This is my hacky solution for not having db changes make django want to make a new migration, as well as having it possible for django to start w/o having a working db connection all the time.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Browser issues - Wagtail's documentation
Some issues with Wagtail may come from the browser. Try clearing the browser cache and cookies, or you can also try using Wagtail...
Read more >
Fix UI issues with Wagtail 4.0 release #558 - GitHub
The majority of the Wagtail admin UI no longer uses uppercase text, to improve readability. The exception is the page status (live, draft, ......
Read more >
Wagtail support - Google Groups
Welcome to the Wagtail support list, for questions, suggestions and help with implementing Wagtail CMS... Mark all as read. Report abusive group.
Read more >
Quick video tour of Wagtail 4.0 - YouTube
Wagtail 4.0 is here. In this video, we'll walk through some of the big changes that have been made and our favorite new...
Read more >
Issues upgrading from wagtail 2.15.6 to 2.16 breaks all pages ...
I am experiencing an issue upgrading a site's wagtail installed version. I have successfully upgraded from 2.12 to 2.15.6 but am running ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found