In our old models, we had a “button url” and a “button page”, inside of our template was the logic to decide which of the buttons to use. We could move that logic, and any other logic we might need, into the streamfield itself.
We are going to create a button streamfield that can either lead to an internal page or an external url!
Inside streams/blocks.py:
class ButtonBlock(blocks.StructBlock):
"""An external or internal URL"""
button_page = blocks.PageChooserBlock(required=False, help_text="If selected, this url will be used first")
button_url = blocks.URLBlock(required=False, help_text='If added, this url will be used')
class Meta:
template = "streams/button_block.html"
icon = "placeholder"
label = "Single Button"
We must add it to the page’s content variable:
class FlexPage(Page):
"""Flexible page class"""
template = "flex/flex_page.html"
content = StreamField(
[
("title_and_text", blocks.TitleAndTextBlock(classname="text_and_title")),
("full_rich_text", blocks.RichTextBlock(classname="full_rich_text")),
("my_rich_text", blocks.MyRichTextBlock(classname="my_rich_text")),
("cards", blocks.CardBlock()),
("cta", blocks.CTABlock()),
("button", blocks.ButtonBlock()),
],
null=True,
blank=True,
use_json_field=True
)
then make a very simple template:
<div class="flex justify-start">
<div class="flex-row">
<div class="text-center">
<a href={{ url }}>Learn More</a>
</div>
</div>
</div>
Right now this template won’t do anything because we have to tell the streamfield which type of button to use.
We have to create a new class, this tutorial did it in blocks.py on top of our new block:
class LinkStructValue(blocks.StructValue):
"""Additional logic for our urls"""
def url(self):
button_page = self.get('button_page')
button_url = self.get('button_url')
if button_page:
return button_page.url
elif button_url:
return button_url
else:
return None
This decides which of the two to use, if it has both, it will return a button_page first!
Now we have to attach the two. Back to the ButtonBlock, we must add a value_class in the Meta
class ButtonBlock(blocks.StructBlock):
"""An external or internal URL"""
button_page = blocks.PageChooserBlock(required=False, help_text="If selected, this url will be used first")
button_url = blocks.URLBlock(required=False, help_text='If added, this url will be used')
class Meta:
template = "streams/button_block.html"
icon = "placeholder"
label = "Single Button"
value_class = LinkStructValue # add this
This very useful for simplifying your template.
<div class="flex justify-start">
<div class="flex-row">
<div class="text-center">
<a href={{ self.url }}>Learn More</a>
</div>
</div>
</div>
self.url will call the value_class LinkStructValue’s url function!
As an extra step, lets add more fields to affect the text and color!