Replace a char in django template before final rendering - django

I want to make all of my asterisks (*) in a template in red color in a Django Template. (E.g. in form labels that * symbol points the field is required. But when putting * in label of a form field it is rendered as black color as usual.
How can I accomplish this by for example registering a filter or tag?
Note that I use some libraries (e.g. bootstrap4 form) and the page is full of {{ }} tags. BUT I want to find&replace all ;black *' with 'red *' in final rendered html page.
EDIT: I use django-bootstrap4 and so I simply use
{% bootstrap_form form %} in my templates. (So not have explicit access to label texts) How can I perform my goal?

This is what works for me
# templatetags/custom_tags.py
from django import template
register = template.Library()
#register.filter
def mark_as_required(text):
return '<span style="color:red">{text}</span>'.format(text=text)
# templates/xxx.html
...
{% load custom_tags %}
...
{{ '*'|mark_as_required|safe }}
...

Related

How can I see all possible attributes of an context variables in django templates

I am trying to use double model form at once in one single view, I am using django-betterforms to merge those, It merged all fields from two model in one single form. I know I can use different class and id to separate them, But I can't extract them in template form, like
{{ form }}
it will place full form in template, I can render all field like this
{% for field in form %}
{{ field }} or anything
{% endfor %}
My question is how can I know all the possible attributes of this field like
{{ field.* }} *=anything
kind of dir(field).
This is a problem I have facing but what will be solution to find all attributes or separate two forms in two side. Basically I need to separate two model, those will save in same time with same view but in front-end those will be different.
Thanks in advance!!!
You create a custom filter:
in templatetags/my_filters.py:
from django import template
register = template.Library()
#register.filter
def getallattrs(value):
return dir(value)
in your template:
{% load my_filters %}
...
{{ field|getallattrs }}

get_FOO_display equivalent for ChoiceBlock

I have a block as follows:
class SomeBlock(blocks.StructBlock):
choice = blocks.ChoiceBlock(choices=(('Y', 'Yellow'), ...))
# more fields
class Meta:
template = 'myapp/blocks/some_block.html'
In my template I have:
{% load wagtailcore_tags %}
<div>{{ value.choice }}</div>
This would display 'Y' as expected but how do I get it to display as 'Yellow'?
These variations do not work (get no output):
{{ value.get_choice_display }}
{{ value.bound_blocks.get_choice_display }}
Unfortunately there isn't a direct equivalent - Wagtail's blocks mechanism treats the display names as a detail that's specific to the edit form, rather than part of the data, so they're not easily accessible at the point where you're rendering the template. I'd suggest arranging things as follows:
Define the choice list in its own module where it can be imported from multiple places - e.g. myapp/colors.py:
COLORS = (('Y', 'Yellow'), ...)
COLORS_LOOKUP = dict(COLORS) # gives you a dict of the form {'Y': 'Yellow', ...}
Update the ChoiceBlock definition to refer to the list defined there:
from myapp.colors import COLORS
class SomeBlock(blocks.StructBlock):
choice = blocks.ChoiceBlock(choices=COLORS)
Create a custom template tag to do the lookup from value to display name - e.g. myapp/templatetags/color_tags.py:
from django import template
from myapp.colors import COLORS_LOOKUP
register = template.Library()
#register.simple_tag
def get_color_display_name(name):
return COLORS_LOOKUP.get(name)
Use this tag in your template:
{% load color_tags %}
...
{% get_color_display_name value.choice %} {# to output it immediately #}
{% get_color_display_name value.choice as label %} {# to assign it to the variable 'label' #}

Wagtail not pulling through custom field panels

I'm overriding the wagtail AbstractFormField panel attribute in the following way:
...
before_input = RichTextField(verbose_name=_('before input'), blank=True)
after_input = RichTextField(verbose_name=_('after input'), blank=True)
panels = [
FieldPanel('label'),
FieldPanel('before_input'),
FieldPanel('after_input'),
FieldPanel('required'),
FieldPanel('field_type', classname="formbuilder-type"),
FieldPanel('choices', classname="formbuilder-choices"),
FieldPanel('default_value', classname="formbuilder-default"),
]
where the other panels are what comes out of the box.
This is working perfectly on the admin side and also saving as rich text into my database
I am pulling this through to my form in my template in the following way:
<form action="{% pageurl page %}" method="POST" class="lm-ls1" id="feedback-form">
{% csrf_token %}
{{ form.question1.help_text }} <!-- Simpler non interable way -->
{{ form.question1.before_input }}
<p>---------------</p>
{% for row in form.fields.values %}
{{row.choices}}
<p>---------------</p>
{{row.help_text}}
<p>---------------</p>
{{row.before_input}}
{% endfor %}
</form>
But I am only getting html output for the form panels excluding the before_input and after_input ones
I am getting through roughly the following:
Overall, how did you feel about the service you received today?
---------------
[('Very satisfied', 'Very satisfied'), ('Satisfied', 'Satisfied'),
('Neither satisfied nor dissatisfied', 'Neither satisfied nor dissatisfied'), ('Dissatisfied', 'Dissatisfied'), ('Very dissatisfied', 'Very dissatisfied')]
---------------
Overall, how did you feel about the service you received today?
---------------
---------------
How can I access the before_input field panel data stored in the _formfield wagtail table?
Bit late but hopefully this still helps you or someone else out there.
How Wagtail Forms Work
Wagtail forms provided to the view context for AbstractFormPage models is a fully instanced Django Form. This means that you will only ever find values in the form that can be given to a Django Form.
This includes fields, which are instances of Django's Fields (eg. CharField) and there is no simple way to add additional attributes to these fields.
You can see how the Form object is built in the Wagtail FormBuilder class definition.
1 - Make a Custom Template Tag
A somewhat simple way to get additional attributes on your FormField (Wagtail's FormField) is using a template tag.
Create a new file in in a folder templatetags in your app, and build a simple_tag that will take the form_page, the field (which will be a Django Field instance) and a string of the attribute name you want to get.
# myapp/templatetags/form_tags.py
from django import template
from django.utils.html import mark_safe
register = template.Library()
#register.simple_tag(name='form_field_attribute')
def form_field_attribute(form_page, field, attribute_name, default=None):
"""Return attribute on FormField where field matches 'field' provided."""
# field is a django Field instance
field_name = field.name
results = [
# if html is stored, need to use mark_safe - be careful though.
mark_safe(getattr(form_field, attribute_name, default))
# get_form_fields() is a built in function on AbstractFormPage
for form_field in form_page.get_form_fields()
# clean_name is property on AbstractFormField used for Django Field name
if form_field.clean_name == field_name]
if results:
return results[0]
return default
2 - Revise your form_page.html Template
In your template, cycle through your form (this is the Django Form instance) and use the template helper to get you the extra attributes you need. Example below, passing in page or self will work the same as they are both the instance of your FormPage.
<form action="{% pageurl page %}" method="POST" role="form">
{% csrf_token %}
{% for field in form %}
<div>{% form_field_attribute page field 'before_input' %}</div>
{{ field }}
<div>{% form_field_attribute page field 'after_input' %}</div>
{% endfor %}
<input type="submit">
</form>

Apply filter to result of Django custom template tag (like i18n trans)

We have a Django project with a legacy translation module that stores translations in the database, and does not use Django i18n (gettext).
We have written custom template tags {% db_trans ... %} that work like Django's default {% trans ... %}, but there is one trick we cannot duplicate.
In this example, "tree" (English) translates to "boom" (Dutch).
In my Django template, if I use default i18n and write {% trans "tree" %} I will see the result boom. When I want to use this as a title, I use the capfirst filter like this {% trans "tree"|capfirst %}, and the result will be Boom. Note the capital B.
This seems to be a special trick. Our db_trans custom tag (based on simple_tag) capitalizes the input before translation. Since there is no translation in the database for Tree, {% db_trans "tree"|capfirst %} renders its default (untranslated) value, Tree.
I now about {% filter capfirst %}...{% endfilter %} and will probably use it if we cannot find an easy solution.
I have checked the Django source code and I saw that {% trans ... %} is not based on simple_tag and does quite a lot of text argument parsing.
My question: is there a package or snippet that allows a filter specified as shown above to be applied to the result of a custom tag?
If you make your template tag a filter, then you can chain it with capfirst built-in filter
from django import template
register = template.Library()
#register.filter
def db_trans(word, request):
return do_the_translation(word, request)
Then in the html it would look like this
{{ 'tree'|db_trans:request|capfirst }}

Force Django template to render curly brace values?

I have this data that comes from a field in the database:
item_list = Links.objects.filter(visible=True)
In an iteration of item_list there is item.name and item.link. In item.link there could potentially be a string value of
'/app/user/{{user.id}}/'.
When rendering this particular item.link in a Django template, it comes out literally in html output as:
/app/user/{{user.id}}/
when literally I am hoping for it to render as:
/app/user/1/
Is there any way to force the template to recognize this as a compiled value for output?
You have to create a custom template tag:
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def render(context, tpl_string):
t = template.Template(tpl_string)
return t.render(context)
And the in your template:
{% load my_tags %}
{% render item.link %}