Cannot make a model @property def-as-field work with wagtail 2.0
Asked Answered
P

1

5

I'm using Wagtail 2.0 with a custom Block that has the following code:

class LinkButtonBlock(blocks.StructBlock):
    label = blocks.CharBlock()
    URL = blocks.CharBlock()
    styling = blocks.ChoiceBlock(
        choices=[
            ('btn-primary', 'Primary button'),
            ('btn-secondary', 'Secondary button'),
            ('btn-success', 'Success button'),
            ('btn-info', 'Info button'),
            ('btn-warning', 'Warning button'),
            ('btn-error', 'Error button'),
        ],
        default='btn-info',
    )
    outline = blocks.BooleanBlock(
        default=False
    )

    @property
    def css(self):
        btn_class = self.styling
        if self.outline is True:
            btn_class = btn_class.replace('btn-', 'btn-outline-')
            return btn_class

    class Meta:
        icon = 'link'
         template = 'testapp/blocks/link_button_block.html'

If I then try to access this css "property" in my template, nothing seems to happen. Putting a print(self) as first line inside the css def also shows nothing on the console suggesting the function never even gets called.

Using the following template:

{% load wagtailcore_tags %}

<a class="btn {{ block.value.css }}" href="{{ block.value.URL }}">{{ block.value.label }}</a>

Simply yields:

<a class="btn " href="actual.url.from.instance">actual.label.from.instance</a>

Also, block.value.styling and block.value.outline on their own work just fine, so... what am I doing wrong here?

Pallette answered 19/3, 2018 at 23:34 Comment(0)
C
14

The thing that's tripping you up is that the value objects you get when iterating over a StreamField are not instances of StructBlock. Block objects such as StructBlock and CharBlock act as converters between different data representations; they don't hold on to the data themselves. In this respect, they work a lot like Django's form field objects; for example, Django's forms.CharField and Wagtail's CharBlock both define how to render a string as a form field, and how to retrieve a string from a form submission.

Note that CharBlock works with string objects - not instances of CharBlock. Likewise, the values returned from StructBlock are not instances of StructBlock - they are a dict-like object of type StructValue, and this is what you need to subclass to implement your css property. There's an example of doing this in the docs. Applied to your code, this would become:

class LinkButtonValue(blocks.StructValue):
    @property
    def css(self):
        # Note that StructValue is a dict-like object, so `styling` and `outline`
        # need to be accessed as dictionary keys
        btn_class = self['styling']
        if self['outline'] is True:
            btn_class = btn_class.replace('btn-', 'btn-outline-')
        return btn_class

class LinkButtonBlock(blocks.StructBlock):
    label = blocks.CharBlock()
    URL = blocks.CharBlock()
    styling = blocks.ChoiceBlock(choices=[...])
    outline = blocks.BooleanBlock(default=False)

    class Meta:
        icon = 'link'
        template = 'testapp/blocks/link_button_block.html'
        value_class = LinkButtonValue
Copycat answered 20/3, 2018 at 0:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.