Skip to content

Instantly share code, notes, and snippets.

@cnk
Created July 22, 2020 14:56
Show Gist options
  • Save cnk/a0eab7c981c7f34127955b384cebcad7 to your computer and use it in GitHub Desktop.
Save cnk/a0eab7c981c7f34127955b384cebcad7 to your computer and use it in GitHub Desktop.
Wagtail link block requiring exactly one of 3 options
class AirspaceRequiredLinkBlock(blocks.StructBlock):
"""
Allows a user to create a link to a Page, a Document, or a relative or absolute URL.
This version requires that you provide one and only one of the destinations above.
NOTE: Due to limitations in CSS, callers of LinkBlock() must not specify a label in the construction arguments.
See the comment in the Meta class for why.
NOTE: Within a template, checking for the existence of `self.link` will always return True because the LinkBlock
object is not falsy, even if it has no contents. To retrieve the value of a LinkBlock, use the {% link_url %}
template tag from airspace_tags. ex:
{% load airspace_tags %}
{% link_url self.link as url %}
{% if url %}
<a href={{ url }}></a>
{% endif %}
"""
page = blocks.PageChooserBlock(
required=False,
help_text="Link to the chosen page. If you are going to a page on this site, use this option if possible. "
"NOTE: you must choose one and only one of 'page', 'document' or 'url'."
)
document = DocumentChooserBlock(
required=False,
help_text="Link to the chosen document."
)
url = blocks.CharBlock(
required=False,
help_text="Link to the given URL. This can be a relative URL to a location your own site (e.g. /example#FAQ1) "
"or an absolute URL to a page on another site (e.g. http://www.caltech.edu). Note: absolute URLs "
"must include the http:// otherwise they will not work."
)
class Meta:
label = 'Link'
form_classname = 'link-block'
def clean(self, value):
result = [] # build up a list of (name, value) tuples to be passed to the StructValue constructor
errors = {}
found_items = []
for name, val in value.items():
try:
result.append((name, self.child_blocks[name].clean(val)))
# Blank values are None for 'page' and 'document' but blank urls are '', so check length of str(val)
# We need to str() the value because if there is a document or page, the classes don't have len() method
if val and len(str(val)) > 0:
found_items.append(val)
except ValidationError as e:
errors[name] = ErrorList([e])
if not found_items:
errors['page'] = ErrorList(["You must provide one of 'page', 'document' or 'url'"])
if len(found_items) > 1:
first_item = result[0][0]
errors[first_item] = ErrorList(["You may provide only one of 'page', 'document' or 'url'"])
if errors:
# The message here is arbitrary - StructBlock.render_form will suppress it
# and delegate the errors contained in the 'params' dict to the child blocks instead
raise ValidationError('Validation error in StructBlock', params=errors)
return self._to_struct_value(result)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment