How to set Field attributes in jinja 2 loop - flask

I am quite new to programming and I need your help. I have a field list of float fields where each one should display a different default value.
I want to change the default attribute by iterating in my template but it's not working.
<div class="form-group">
{% for entry in form.pde_parameters %}
{% set entry.default=pde_parameter_value_list[loop.index0]%}
{{ pde_parameter_list[loop.index0] }}
{{ entry.hidden_tag() }}
{{ render_field(entry.parameter_value) }}
{% endfor %}
</div>
class NewModelParameterMaskForm(FlaskForm):
parameter_value = FloatField("")
class Solution(FlaskForm):
pde_parameters = FieldList(FormField(NewModelParameterMaskForm), min_entries=1)
sde_parameters = FieldList(FormField(NewModelParameterMaskForm), min_entries=1)
I get this error message:
jinja2.exceptions.TemplateRuntimeError: cannot assign attribute on non-namespace object

You need to populate the form before you pass it to Jinja, not inside your template.
def show_solution():
form = Solution(request.form)
if form.validate_on_submit():
# Use form values
return redirect(url_for('done'))
for s in solutions(): # Fetch your solutions from somewhere
form.pde_paramaters.append_entry({'parameter_value': s.float_value})
return render_template('solution.html', form=form)

I was receiving the same error. The solution I found would look similar to this in the question's case:
{% set update_result = entry.update({'default': pde_parameter_value_list[loop.index0]}) %}
Then ignore the update_result variable and use the entry normally.
It seems Jinja doesn't allow setting directly the value of a field/property inside an object. However, if these are dictionaries or lists, you can use methods like update or append since they change the object internally.

Just as with the dict.update suggestion above, it seems to be possible to trick Jinja into setting attributes by simply calling __setattr__ on the object:
{% set _ = obj.__setattr__('fieldname', 'value') %}

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 }}

Why won't Django template recognize list from object field?

I have an object field that stores a list. The Django template is treating the list like a string, it iterates over each character, not each list value.
Tried saving the object field in various ways, "['1','2']" and "1,2". Tried the "|make_list" filter.
models.py
class SpecialField(models.Model):
name = models.CharField(max_length=200,blank=True,null=True)
description = models.CharField(max_length=200,blank=True,null=True)
value_options = models.CharField(max_length=200,blank=True,null=True)
# value_options might be "['1','2']" or "red, green, blue"
views.py
object_field_list= SpecialField.objects.all()
context = {
'object_field_list': object_field_list,
}
return render(request, 'app1/template-detail.html', context)
template
{% for object_field in object_field_list%}
{% for list_value in object_field.value_options %}
<option>{{ list_value }}</option>
{% endfor %}
{% endfor %}
I was hoping for:
<option>1</option>
<option>2</option>
But I am getting:
<option>[</option>
<option>'</option>
<option>1</option>
<option>'</option>
<option>,</option>
<option>'</option>
<option>2</option>
<option>'</option>
<option>]</option>
You are wanting to show data from a model, so let's suppose you have a model
class SpecialField(models.Model):
name=models.CharField(max_length=101)
price = models.IntegerField(default=0)
Given the way you've set in in context you can show this in the template with
{% for obj in object_field_list %}
{{ obj.name}} - {{ obj.price }}
{% endfor %}
Obviously, you need to amend for your model
Tweak on your suggestion...it needs one more level of looping. Still can't get it to work.
models.py
class SpecialField(models.Model):
name=models.CharField(max_length=101)
price = models.IntegerField(default=0)
custom_list = models.CharField(max_length=200)
template
{% for obj in object_field_list %}
{{ obj.name}} - {{ obj.price }}
{% for list_value in obj.custom_list %}
{{ list_value }}
# this is where it's breaking for me
{% endfor %}
{% endfor %}
context = {
'object_field_list': SpecialField.objects.values_list('value_options', flat=True),
}
Should get you what you actually want to loop over.
EDIT: Sorry, I missed the fact you are storing a string rather than using an ArrayField or similar. The problem from your updated answer is the data you have isn't consistent. If it were simply all comma-separated values you could do:
object_field_list = [value_list.split(',') for value_list in SpecialField.objects.values_list('value_options', flat=True)]
but you will need some way of normalizing the data you're storing in value_options. How does the data get into the database? If it's via Django, you can apply some kind of cleaning method on the form or API endpoint that accepts the data.
Tried saving the object field in various ways, "['1','2']" and "1,2". Tried the "|make_list" filter.
If you have complete control over the incoming data, you would be better off normalizing the data: rather than storing a single value_options entry on SpecialField, you would remove that field and add a second model, e.g., SpecialFieldOption like
class SpecialFieldOption(models.Model):
name = models.CharField(max_length=200, blank=False)
field = models.ForeignKey(SpecialField, related_name='options')
# now you can do
SpecialField.objects.get(pk=1).options.all()
to loop over the list of all options for a given SpecialField.
This was the handiest solution...define a new list using split. https://stackoverflow.com/a/8318915/9268133. Thanks for everyone's help!

dynamic form in Flask

in a movie rating app,I would like to generate a WTF form in flask with dynamic number of fields. i.e, if there are three movies, there will be three fields.
I thought about a few options, but none of them worked:
class RatingForm(Form):
rating = TextField("rating",[validators.Length(min=1, max=1)])
movie_order=TextField("movie",[validators.Length(min=1, max=1)])
submit = SubmitField("submit rating")
pass a parameter to the form object - I don't see how can I pass a parameter to this kind of class
make a loop inside the template, thus generate and return multiple forms, and choose the correct one. this also doesnt work, since the request.form is immutableDict, and I end up having multiple fields with the same key, which I cant access.
{% for movie in movies_to_rate %}
<p>
<form method="POST" enctype="multipart/form-data" action="/rate">
{{ movie}}
{{ forms[movie].rating}}
{{ forms[movie].submit }}
<input type="submit" value="Go">
</p> {% endfor %}
any ideas about what can I do?
I think you can generate a list of TextField's as a class member instead of using one field object. (Though it looks a bit weird, I assume your validators are what you meant.)
class RatingForm(Form):
def __init__(self, count):
self.ratings = [TextField("rating_" + str(i), [validators.Length(min=1, max=1)])
for i in range(count)]
...

Django template - dynamic variable name

Good Afternoon,
How can I use a variable variable name in Django templates?
I have a custom auth system using context, has_perm checks to see if the user has access to the specified section.
deptauth is a variable with a restriction group name i.e SectionAdmin. I think has.perm is actually checking for 'deptauth' instead of the variable value SectionAdmin as I would like.
{%if has_perm.deptauth %}
How can I do that? has_perm.{{depauth}} or something along those lines?
EDIT - Updated code
{% with arg_value="authval" %}
{% lookup has_perm "admintest" %}
{% endwith %}
{%if has_perm.authval %}
window.location = './portal/tickets/admin/add/{{dept}}/'+val;
{% else %}
window.location = './portal/tickets/add/{{dept}}/'+val;
{%endif%}
has_perm isn't an object.. it's in my context processor (permchecker):
class permchecker(object):
def __init__(self, request):
self.request = request
pass
def __getitem__(self, perm_name):
return check_perm(self.request, perm_name)
You're best off writing your own custom template tag for that. It's not difficult to do, and normal for this kind of situation.
I have not tested this, but something along these lines should work. Remember to handle errors properly!
def lookup(object, property):
return getattr(object, property)()
register.simple_tag(lookup)
If you're trying to get a property rather than execute a method, remove those ().
and use it:
{% lookup has_perm "depauth" %}
Note that has_perm is a variable, and "depauth" is a string value. this will pass the string for lookup, i.e. get has_perm.depauth.
You can call it with a variable:
{% with arg_value="depauth_other_value" %}
{% lookup has_perm arg_value %}
{% endwith %}
which means that the value of the variable will be used to look it up, i.e. has_perm.depauth_other_value'.
You can try like this,
{{ dict|key:key_name }}
Filter:
def key(d, key_name):
return d[key_name]
key = register.filter('key', key)
More information, django ticket

Muliple instances of modelform

I have a list in my template. For each item in the list, I have a {{ modelform }} that contains a checkbox. I can check the box and it updates as should. The problem is that when I check the box for one item and submit, it submits for all of the checkboxes because they are the same in each instance. Is there a way to set up a unique checkbox instance for each item in the list?
Current each modelform checkbox renders the same like this:
<input name="is_solution" type="checkbox" class="is_solution" id="is_solution">
I also tried using
test = request.POST.get('checkbox')
and
test = request.POST.get('checkbox')
thinking that using this I might be able to post an update in my view. I think I am going about this all wrong and I am lost. Essentially, I would like to have a checkbox on a list much like here on stackexchange where you can confirm an answer. Any suggestions?
You have to use form's prefix in the view like (just something unique for each form object):
def foo(request, ...):
objs = Model.objects.filter(...)
forms = []
for i, obj in enumerate(objs):
form = ModelForm(instance=obj, prefix=str(i))
forms.append(form)
...
This will make sure each form has unique identifier, hence you will be able to submit a specific form.
And you can render the forms like usual in the template:
<form ...>
{% csrf_token %}
{% for form in forms %}
{{ form }}
{% endfor %}
</form>