Reputation: 1137
I have this model above, and I would like to remove the title. Or rename it. Actually the match number itself is a title of this model. So I need one of the following options:
My model:
class Match(Page):
match_number = models.PositiveSmallIntegerField(blank=True)
team_1 = models.ForeignKey(
TeamRooster,
null=True, blank=True,
on_delete=models.SET_NULL,
related_name="+",
)
team_1_color = ColorField(default='#ff0000', blank=True)
team_1_score = models.PositiveSmallIntegerField(blank=True)
team_2 = models.ForeignKey(
TeamRooster,
null=True, blank=True,
on_delete=models.SET_NULL,
related_name="+",
)
team_2_color = ColorField(default='#0066ff', blank=True)
team_2_score = models.PositiveSmallIntegerField(blank=True)
match_starts_at = models.DateTimeField()
parent_page_types = ['Matches']
content_panels = [
FieldPanel('title'),
FieldPanel('match_number', classname="6"),
FieldPanel('match_starts_at', classname="6"),
MultiFieldPanel([
FieldPanel('team_1', classname="12"),
FieldPanel('team_1_color', classname="6"),
FieldPanel('team_2_score', classname="6"),
], heading="Team 1"),
MultiFieldPanel([
FieldPanel('team_2', classname="12"),
FieldPanel('team_2_color', classname="6"),
FieldPanel('team_2_score', classname="6"),
], heading="Team 2"),
]
Upvotes: 3
Views: 5465
Reputation: 5225
To tackle the issue spotted by @Mark Chackerian you could define a custom base_form_class
for your wagtail
page.
Now, instead of overwriting the clean function of the model you could hook into the save
method of your custom form. So you don't have to set a default slug for EVERY wagtail page:
forms.py
:
from django import forms
from django.utils.translation import gettext_lazy as _
from django.utils.text import slugify
from django.utils.html import strip_tags
from wagtail.admin.forms import WagtailAdminPageForm
class NoTitleForm(WagtailAdminPageForm):
title = forms.CharField(required=False, disabled=True, help_text=_('Title is auto-generated.'))
slug = forms.SlugField(required=False, disabled=True, help_text=_('Slug is auto-generated.'))
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self.initial['title']:
self.initial['title'] = _('auto-generated-title')
if not self.initial['slug']:
self.initial['slug'] = _('auto-generated-slug')
def save(self, commit=True):
page = super().save(commit=False)
new_title = strip_tags(self.cleaned_data['rtf_title'])
page.title = new_title
page.slug = slugify(new_title)
if commit:
page.save()
return page
models.py
:
# ... imports omitted for brevity
class PageWithRtfTitle(Page):
base_form_class = NoTitleForm
rtf_title = RichTextField()
content_panels = Page.content_panels + [
MultiFieldPanel(
[
FieldPanel('rtf_title'),
],
heading=_('Metadata')
)]
# ...
Still, this solution may not be ideal for some use cases. Both approaches overwrite the slug every time the title changes. You may not want that - but you could adapt the code to your needs.
Upvotes: 1
Reputation: 5196
A way to achieve this is to actually remove the title
from the content_panels
. Then manually create your title
and slug
fields inside your Page's clean
method. You will also need to set a default slug
value for your model.
from django.utils.text import slugify
# all other imports (Page, models, panels etc)
class MatchPage(Page):
"""A page that represents a single game match."""
number = models.IntegerField(
unique=True, # must be unique for use in slug
help_text="Add the unique number of this Match.")
starts_at = models.DateTimeField(
blank=True, # allows blank values (ie. not required)
help_text="Date and time of when this Match starts.")
content_panels = [
# title not present, title should NOT be directly editable
MultiFieldPanel([
FieldRowPanel([
FieldPanel('number', classname="col6"),
FieldPanel('starts_at', classname="col6"),
]),
], 'Match'),
# other field panels... teams etc
]
def clean(self):
"""Override the values of title and slug before saving."""
# super(MatchPage, self).clean() # Python 2.X syntax
super().clean()
new_title = 'Match %s' % self.number
self.title = new_title
self.slug = slugify(new_title) # slug MUST be unique & slug-formatted
# set a default blank slug for when the editing form renders
# we set this after the model is declared
MatchPage._meta.get_field('slug').default = 'default-blank-slug'
This is can be a confusing issue with Wagtail as the Page
model (separate from your MatchPage
model) must have certain fields (title, slug). The editing interface also builds the slug field on the client side (in Javascript), and it relies on the title
field being present.
So you will have to take care of both generating the title
and the slug
field yourself. It is important that the slug field is both unique and slugified.
In the example code there is no consideration of the editor manually setting the slug themselves (in the Promote panel), no matter what the user sets the slug to, it will always override to match-123
.
The title
field can be any string, but is required.
Remember that Wagtail does not dictate how you use the title
field in any way in how you render your templates. It is only really the model restrictions and the admin interface that assumes the existence of a title.
There is an old and ongoing discussion on the Wagtail Github page about this item: https://github.com/wagtail/wagtail/issues/161
MySomethingPage
with Page
at the end, this helps you and other developers know that this thing is a Page.match_something
or match_other_thing
). This makes it confusing down the road as you will have to be typing match_page.match_number
when match_page.number
makes more intuitive sense. Or if you do use this convention, use it on every single field like match_page.match_team_1
.InlinePanel
, it will mean much less duplication of code. See docs about InlinePanels & Model ClustersUpvotes: 13