How to iterate WTForms FieldList with Jinja2 - jinja2

As the title says, here's what I've got:
form = F(obj = myobject)
myfieldlist= FieldList(FormField(form))
{% for subfield in form.myfieldlist %}
{{ subfield.field }}
{{ subfield.label }}
{% endfor %}
This outputs nothing, any ideas? Also, not entirely sure if FormField is required.
Thanks

FormField takes a class not an instance:
class GuestForm(Form):
email = TextField()
vip = BooleanField()
class VenueForm(Form):
name = TextField()
guests = FieldList(FormField(GuestForm))
Then in your controller:
form = VenueForm(obj=myobject)
render("template-name.html", form=form)
In your template you will need to iterate over the FieldList field as if it was its own form:
{% for guest_form in form.guests %}
<ul>
{% for subfield in guest_form %}
<li>{{ subfield.label }} {{ subfield }}</li>
{% endfor %}
</ul>
{% endfor %}

Related

Retrieve first image from a Wagtail Streamfield for blog_index_page

Assume I am following the Your first Wagtail site tutorial, except I want to use a Streamfield, instead of separate RichTextField or BlogPageGalleryImage. For example :
class BlogPage(Page):
date = models.DateField("Post date")
intro = models.CharField(max_length=250)
body = RichTextField(blank=True)
... becomes ...
class BlogPage(Page):
body = StreamField([
('heading', blocks.CharBlock(classname="full title")),
('intro', blocks.RichTextBlock()),
('body', blocks.RichTextBlock()),
('image', ImageChooserBlock()),
], null=True, blank=True,)
How can I (or can I) pull the first image and the beginning of intro (...as I cannot restrict length) from a Streamfield for use in the blog_index_page.html template, and avoid using separate fields and BlogPageGalleryImage Class? Therefore replacing this template :
{% for post in blogpages %}
{% with post=post.specific %}
<h2>{{ post.title }}</h2>
{% with post.main_image as main_image %}
{% if main_image %}{% image main_image fill-160x100 %}{% endif %}
{% endwith %}
<p>{{ post.intro }}<p>
{% endwith %}
{% endfor %}
With something more like this :
{% for post in blogpages %}
{% with post=post.specific %}
<h2>{{ post.title }}</h2>
{{ post.body.image.first() }}
<p>{{ post.intro.get_words(50) }}</p>
{{ post.body|richtext }}
{% endwith %}
{% endfor %}
Many thanks.

Why do I not get labels rendered from CharField when using DJANGO formset

I have a form with 2 CharFields. Both have label="xyz".
If I use this form in a formset, the lables are not shown in the HTML
I have tried looking at the rendered HTML and the label is missing. I have tried just a form and that works.
Forms:
class WindingVoltsSpecifier(forms.Form):
winding_name = forms.CharField(max_length=20, label="Winding name")
voltages = forms.CharField(max_length=20, label="Voltages")
View:
def add_mains_transformer_primary_configs(request):
# Add a new config
# Create the formset, specifying the form and formset we want to use.
# From https://whoisnicoleharris.com/2015/01/06/implementing-django-formsets.html
VoltsSpecifierFormSet = formset_factory(WindingVoltsSpecifier)
if request.method == 'POST':
pass
else:
mt_config_form = MainsTransformerConfiguration()
volts_formset = VoltsSpecifierFormSet()
context = {
'mt_config_form' : mt_config_form,
'volts_formset' : volts_formset,
}
return render(request, 'designer/mains_configs.html', context)
Template:
{% extends 'designer/base.html' %}
{% load crispy_forms_tags %}
{% block title %}configuration{% endblock %}
{% block content %}
{% load static %}
<h1>Configuration</h1>
<form method="post">
{% csrf_token %}
{{ mt_config_form|crispy }}
{{ volts_formset.management_form|crispy }}
{% for volts_form in volts_formset %}
<table>
{% for form in volts_form %}
{{ form }}
{% endfor %}
<table>
<!--<div class="volts-formset">
{{ volts_form.winding_name }}
{{ volts_form.voltages }}
</div>
-->
{% endfor %}
{% if volts_formset.non_form_errors %}
{% for error in volts_formset.non_form_errors %}
{{ error|escape }}
{% endfor %}
{% endif %}
<input type="submit" value="Update Profile" class="button" />
</form>
<script>
$('.volts-formset').formset({
addText: 'add winding',
deleteText: 'remove'
});
</script>
{% endblock %}
I would expect the label to be beside the field.
Labels are not rendered with fields, you will have to do a {{ FIELD.label_tag }}.
It will be something like this:
<table>
{% for field in volts_form %}
{{ field.label_tag }}
{{ field }}
{% endfor %}
<table>
More info: Looping over the forms fields - Django documentation
Hope this solution to works!
{% for field in volts_forms %}
{{ field.name}} <!-- label name -->
{{ field }}<!-- ex. input -->
{% endfor %}

How can I concatenate a string using Jinja for loop?

I am trying to iteratively concatenate a string to build url params with a 'for' loop, but I believe I am having scoping issues.
The output should be: url_param = "&query_param=hello&query_param=world"
array_of_objects = [{'id':'hello'},{'id':'world'}]
{% set url_param = "" %}
{% set array_of_ids = array_of_objects|map(attribute='id')|list%} // correctly returns [1,2]
{% for id in array_of_ids %}
{% set param = '&query_param='~id %}
{% set url_param = url_param~param %}
{% endfor %}
//url_param is still an empty string
I also tried namespace(), but to no avail:
{% set ns = namespace() %}
{% set ns.output = '' %}
{% set array_of_ids = array_of_objects|map(attribute='id')|list%} // correctly returns [1,2]
{% for id in array_of_ids %}
{% set param = '&industries='~id%}
{% set ns.output = ns.output~param %}
{% endfor %}
//ns.output returns namespace
That is indeed a scope issue. One "hacky" way of dealing with this is using a list that you append to like so:
{% set array_of_objects = [{'id':'hello'},{'id':'world'}] %}
{% set array_of_ids = array_of_objects|map(attribute='id')|list%}
{{ array_of_ids|pprint }} {# output: ['hello', 'world'] #}
{% set ids = [] %} {# Temporary list #}
{% for id in array_of_ids %}
{% set param = '&query_param='~id %}
{% set url_param = url_param~param %}
{{ ids.append(url_param) }}
{% endfor %}
{{ ids|pprint }} {# output: [u'&query_param=hello', u'&query_param=world'] #}
{{ ids|join|pprint }} {# output: "&query_param=hello&query_param=world" #}
The above gets you what you need, but for this specific example I would take a look at using jinja's join filter. It's more declarative and feels a little less hacky.
{% set array_of_objects = [{'id':'hello'},{'id':'world'}] %}
{# set to a variable #}
{% set query_string = "&query_param=" ~ array_of_objects|join("&query_param=", attribute="id") %}
{{ query_string|pprint }}
{# output: u'&query_param=hello&query_param=world' #}
{# or just use it inline #}
{{ "&query_param=" ~ array_of_objects|join("&query_param=", attribute="id") }}
You should change the initialization of your namespace.
Here is an example from the docs that will help you out:
{% set ns = namespace(found=false) %}
{% for item in items %}
{% if item.check_something() %}
{% set ns.found = true %}
{% endif %}
* {{ item.title }}
{% endfor %}
Found item having something: {{ ns.found }}

how to use django widget tweaks and combine template class string and widget attr attributes string name

I am trying to customise a django form for use with bootstrap 4, custom html layout & per field class or id names on the FormModel defintion
I have the following html
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
{% if form.non_field_errors %}
<div class="alert alert-danger" role="alert">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
{% for field in form.visible_fields %}
<div class="form-group">
{{ field.label_tag }}
{% if form.is_bound %}
{% if field.errors %}
{% render_field field class="form-control is-invalid" %}
{% for error in field.errors %}
<div class="invalid-feedback">
{{ error }}
</div>
{% endfor %}
{% else %}
{% render_field field class="form-control is-valid" %}
{% endif %}
{% else %}
{% render_field field class="form-control" %}
{% endif %}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
{% endfor %}
And the following form defintion:
class DocumentForm(forms.ModelForm):
field1 = PartLookupField(required=True, widget=forms.TextInput(attrs={'class': 'field1-choice-ajax'}))
field2 = forms.CharField(required=True, widget=forms.TextInput(attrs={'id': 'field2-field'}))
form_lines = forms.CharField(widget=forms.HiddenInput())
class Meta:
model = Document
fields = ("field1", "field2", "form_lines")
So essentially, I need to get the per field definition of id or class, from the widget on the model, and combine that with the template defined form-control or is-valid/invalid classes in the template.
I've tried going down this route
How to concatenate strings in django templates?
But it just seems like it's going to end up in a huge mess.
Essentially, how can I combine template defined attributes and per field defined attributes? I need to end up with class="form-control field1-choice-ajax" for the field specified in the model (and the correct extra class names for the valid/invalid states).
Previously I was using the bootstrap4 form library, but I need complete control now:
{% csrf_token %}
{% bootstrap_form form %}
I've created my own template filter in order to add class attributes to existing form fields:
#register.filter
def add_class(field, css):
"""Add a class to a field in a template.
Example:
> {{ form.my_field|add_class:"required" }}
<input id="my_field_id" name="my_field" class="required" type="text">
Args:
field: this should be a form field, of type ``BoundField``
css: this should be a string with one or more class names separated by spaces
"""
class_old = field.field.widget.attrs.get('class', None)
class_new = class_old + ' ' + css if class_old else css
return field.as_widget(attrs={'class': class_new})
So now I can do this in a template:
{{ field|add_class:"is-valid" }}
Use Widget Tweaks.
It allows you to do something like:
{{ field|add_class:'form-control' }}

Iterating Through Many to Many Model

I have a model like so:
`class task(models.Model):
name = models.CharField(max_length=100)
notes = models.TextField()
created = models.DateTimeField()
created_by = models.ForeignKey(User)
subtask = models.ManyToManyField('self')
`
I'm having trouble writing out the view for it, essentially
<li>task #1</li>
<li>subtask#1.1</li>
<li>subtask#1.2</li>
<li>task #2</li>
<li>subtask#2.1</li>
<li>subtask#2.2</li>
.
.
.
.
I tried creating a for loop to iterate through them using but it doesn't show up nested like how I would want it
{% for task in items %}
<li>{{ task.name }}</li>
{% for subtask in task %}
<li>{{ subtask.name }}</li>
{% endfor %}
{% endfor %}
This is an HTML issue rather than with your django code
{% for task in items %}
<li>{{ task.name }}
<ul>
{% for subtask in task.subtask.all %}
<li>{{ subtask.name }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}