I have a CRUD application where wtforms is being used to populate a series of forms with a 'for' loop to allow users to edit existing data in the DB. I'm using "value=row.XXX" to pre-populate the form with existing data from the DB as a default. This works well for normal StringFields, but doesn't work for SelectField. Can anyone help!?
Example html below. The form_edit.group is a SelectField. Annoyingly, when displayed in the form, it defaults to the first item in the 'choices' list rather than the previously chosen data (value=row.group doesn't work as it does for StringFields). This means that when the user comes to resubmit the form it defaults to this first item.
<div class="form-group col-sm-6">
{{ form_edit.description.label }}
{{ form_edit.description(class="form-control", value=row.description) }}
{% for error in form_edit.description.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</div>
<div class="form-group col-sm-6">
{{ form_edit.group.label }}
{{ form_edit.group(class="form-control", value=row.group) }}
{% for error in form_edit.group.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</div>
</div>
<div class="row">
<div class="form-group col-sm-6">
{{ form_edit.qty.label }}
{{ form_edit.qty(class="form-control", value=row.qty) }}
{% for error in form_edit.qty.errors %}
<span style="color: red;">[{{ error }}]</span>
Forms:
class Areas_form(FlaskForm):
hidden_id = HiddenField('Area id')
description = StringField('Description',validators=[DataRequired()])
group = SelectField('Group',validators=[DataRequired()],choices=['Ext(Am) Wall','Ext(Gnd) Wall','Roof(Am)','Flr Slab','Flr(Am)'])
qty = FloatField('Quantity', validators=[DataRequired(message='must be a number')], default=float(1))
lth_a = FloatField('Length a', validators=[DataRequired(message='must be a number')])
lth_b = FloatField('Length b', validators=[DataRequired(message='must be a number')])
assembly = SelectField('Assembly',validators=[DataRequired()], choices=[])
dev_nth = FloatField('Deviation from North', validators=[InputRequired(message='must be a number')])
ang_hor = FloatField('Angle from horizontal', validators=[InputRequired(message='must be a number')])
submit = SubmitField('Submit data')
Example row:
When I press "edit" the following form comes up. The "group" "Flr Slab", has defaulted back to the first item in the choices list - "Ext(Am) Wall".
By contrast the "description" field has pulled "Floor 1" from the database.
Update your SelectField difinition in class Areas_form(FlaskForm): as follows:
group = SelectField('Group',validators=[DataRequired()],choices=[(1,'Ext(Am) Wall'),(2,'Ext(Gnd) Wall'),(3,'Roof(Am)'),(4,'Flr Slab'),(5,'Flr(Am)')])
where we assign the value option as well as the appeared text, and finally, then in your code after form submission, you get the value as regular column fields
Notice, you will use numbers 1,2,3,4, or 5 as a mapping for your choices in select, which would be better.
Adding upon commit
In order to set the default value update
{{ form_edit.group(class="form-control", value=row.group) }}
to
{{ form_edit.group(class="form-control", value=row.group.data) }}
if it does not work, check that data type of type(row.group.data) in python, if it is string type, update the group column difiniation to:
group = SelectField('Group',validators=[DataRequired()],choices=[('1','Ext(Am) Wall'),('2','Ext(Gnd) Wall'),('3','Roof(Am)'),('4','Flr Slab'),('5','Flr(Am)')])
In case, we force to use integer value, add coerce=int to the end of my first suggestion before additions as follows:
group = SelectField('Group',validators=[DataRequired()],choices=[(1,'Ext(Am) Wall'),(2,'Ext(Gnd) Wall'),(3,'Roof(Am)'),(4,'Flr Slab'),(5,'Flr(Am)'),coerce=int])
looking for to hearing from you :)
Good Luck
Related
I'm in need of a custom tag where multiple keyword arguments can be passed in for the purpose of creating an unique id for a given object instance. Whatever string the tag returns it assigns the string as a context variable to the included template. Yet the id context variable remains empty. In this case, the ids for the SVG elements only return the hardcoded strings upvote_ and downvote_.
How can I fix this so that the id context variable is interpolated into the string of the SVG id attributes?
An example being: upvote_queston101
{% for answer in question.answers.all %}
{% include "./posted.html" with post=answer id=set_id question=question.id answer=answer.id %}
{% endfor %}
<div class="rating_box">
<div class="vote">
<svg id="upvote_{{ id }}" class="vote_button">
</svg>
<p>{{ post.score }}</p>
<svg id="downvote_{{ id }}" class="vote_button">
</svg>
</div>
<p class="post_body_content">{{ post.body }}</p>
</div>
from django import template
register = template.Library()
#register.simple_tag
def set_id(*args, **kwargs):
if kwargs['question'] and kwargs['answer']:
q_id, a_id = kwargs['question'], kwargs['answer']
return f"question{q_id}_answer{a_id}"
q_id = kwargs['question']
return f"question{q_id}"
Try to make it more step-by-step and assign temp variable for ids. Suppose there is some conflict with inline usage of "=" signs
{% for answer in question.answers.all %}
{% set_id question=question.id answer=answer.id as ids %}
{% include "./posted.html" with post=answer id=ids %}
{% endfor %}
I have looked online and found that I can use macros to render different content to my modal. Yet there is something missing in my code that prevents it from getting updated and I'm not sure of what exactly.
#app.route("/candidates")
def candidate_fun():
mapping={
"001":"Bangalore",
"002":"Delhi",
"003":"Chennai",
"004": "Mumbai",
"005":"Kolkata",
"006":"Hyderabad"
}
myclient=pymongo.MongoClient(uri)
mydb = myclient["codefundo"]
mycol=mydb['cand_reg']
result=[]
for x in mycol.find():
obj=NewsSearch()
# import pdb; pdb.set_trace()
t=dict(x)
t['a'],t['b'],t['c']=obj.news_candidate(search_term=str(x["First Name"].lower()+" "+x["Last Name"].lower()+ " election"))
# t['News']=str(a+". "+b+". "+c)
result.append(t)
# result.append({'News':obj.news_candidate(search_term=str(result["First Name"]+" "+result["Last Name"]+" election"))})
return flask.render_template("candidate.html",result=result,mapping=mapping)
While the python code isn't of significance, I have provided it to show that I am passing a list result of type dict.
HTML Jinja
<!--MODAL PART-->
{% macro render_modal(a,b,c) -%}
div class="modal-body">
<p>{{a}}</p>
<p>{{b}}</p>
<p>{{c}}</p>
</div>
{%- endmacro%}
<!-- Jinja to make a call -->
{% for key in result %}
<div class="col-6">
<button type="button" class="btn btn-info" data-toggle="modal" data-target="#exampleModalLong">Info</button>
{{render_modal(key['a'],key['b'],key['c'])}}
<!-- Just to test if the value sent is received {{key['a']}} -->
</div>
{% endfor %}
It returns the same data over the modal box for all entries being passed. I want for it to show me the specific values - key[a], key[b], key[c] for every new key in the iteration.
Similar question here: Passing a row ID in a table to a modal in flask (python)
You need to set an id on your modal and refer to that specific id in your button to identify a specific modal. In order for each modal to render different inputs, you need to set different ids for each button-modal group.
In your case, add a unique identifier for each key object in addition to your data entries, e.g.
# in the routes
t['id'] = # unique id identifying the modal
t['a'] = ...
t['b'] = ...
# etc.
Then in the for-loop that calls the modal macro, include the unique id in some way on the data-target attribute of your button:
{% for key in result %}
<div class="col-6">
<button type="button" data-target="#exampleModalLong{{ key.id }}" class="btn btn-info" data-toggle="modal">
Info
</button>
{{ render_modal(key) }}
</div>
{% endfor %}
Use the exact same name you used for data-target for the id of your modal div:
{% macro render_modal(key) -%}
<div class="modal-body" id="exampleModalLong{{ key.id }}">
<p>{{ key.a }}</p>
<p>{{ key.b }}</p>
<p>{{ key.c }}</p>
</div>
{%- endmacro%}
Note that this will inject a lot of HTML into your code if you have a lot of entries in your for-loop. As mentioned in the Stack Overflow post linked at the top, you can consider defining just one modal, and use AJAX to change the modal content.
My group and I are trying to develop a "very-dynamic" web app with Django.
We have a "configurations" template for the users where everyone can configure his/her own space.
Every configuration option should be read from the DB.
view:
def config(request):
if 'logged' not in request.session:
return redirect(login)
apps_list = App.objects.all()
varS = varsesion(request)
context = Configurations.objects.all()
for row in context:
row.form = eval(row.form) #Not sure at all if this is ok
print (row.form)
context.App = 'config'
return render(request, "index.html", {'context': context, 'apps_list ': apps_list , 'varS': varS,
'context.App': context.App})
And in our DB we have the Configurations model table like this:
+----+-----+-----+-----+---------+---------------+
| id |user |Title|Body |OptionBtn| form |
+----+-----+-----+-----+---------+---------------+
| N |userA| X | X | X | DataConfig() |
+----+-----+-----+-----+---------+---------------+
| N2 |userA| X | X | X | ColorsConfig()|
+----+-----+-----+-----+---------+---------------+
| N3 |userA| X | X | X |ButtonsConfig()|
+----+-----+-----+-----+---------+---------------+
And many other columns that I'll skip...
Of course every Form value in the DB's form field exists in forms.py with their respective names.
The problem comes when I try to iterate these forms fields in our template (every other column from the DB is displayed properly like buttons, titles, texts, etc)
Here is the template (I've skipped some of the attrs in the example table):
<div class="breadcrumb">
<li class="breadcrumb-item">
<h1 style="color:#871830">Configurations panel</h1>
</li>
</div>
{% for configuration in context %}
<div style="" id="panel{{ configuration.codConfi }}" class="breadcrumb form-inline">
<div class="form-group col-12" style="color:#228718"><h3>{{ configuration.title}}</h3></div>
<div class="form-group col-3 m-1">
<label class="form-group">{{ configuration.body }}</label>
</div>
<button id="btn{{ configuration.codConfi }}" type="submit" class="btn btn-info ml-auto mr-0 mr-md-3 my-2 my-md-4">{{ configuration.OptionBtn }}</button>
</div>
All this is displayed properly and perfect. But when it comes to the form DB colum...
<!-- _______________________Forms iterations__________________________ -->
<form style="display:none" id="frm{{ configuration.codConfi }}" class="breadcrumb form-inline" action="/Config/{{ configuration.codConfi }}" method="POST" enctype="application/x-www-form-urlencoded">{% csrf_token %}
<div class="form-group col-12" style="color:#228718"><h3>Configure {{ configuration.Title }}</h3></div>
{% for field in configuration.form %}
<div class="form-group col-3 m-1">
<label class="form-group">{{ field.label }}: </label>
{{ field }}
</div>
{% endfor %}
<button type="submit" class="btn btn-success ml-auto mr-0 mr-md-3 my-2 my-md-4">Apply changes</button>
</form>
{% endfor %}
(If you see some attrs that are not showed in my example table it's just because I haven't typed all of them).
Instead of displaying the actual forms properly, it is displaying the value of form DB's column as a string. For instance, for the first value in form column (DataConfig()) it is displaying every letter as a string in the iteration (first it displays "D", then "a", then "t", etc until the last ")").
How can I tell Django it is not a String value but a variable?
On your Django model Configuration you could add a property that fetches and instanciates the actual form class.
For example:
class Configuration(models.Model):
...
def get_form(self):
# 'forms_module' is the Python module (file) that holds the form classes
# 'self.form' is a string that holds the name of the form class
form_class = getattr(forms_module, self.form)
# create an (un-bound) instance of the form
return form_class()
Then, in your template (assuming that configuration is an instance of the model Configuration) you can change this line:
{% for field in configuration.form %}
into this
{% for field in configuration.get_form %}
Note: for this to work, you will need to store the form class name without the parethesis in your DB field (or remove the parethesis before calling getattr(forms_module, self.form)).
If you need something more specific you will need to add more info to your question.
I'm using Django forms with a RadioSelect widget for one of the fields - field1.
Rendering the form in the template as {{ form.field1 }} doesn't raise any errors (i.e. not an invalid choice), but I want to add a little bit more customisation to the radio select widget.
The following code is the general for loop that I'm writing in my template to loop over each choice to style them...
{% for choice in acc.field1 %}
<label>
<div class='...'><input type='radio' name='field1'/></div>
<div class='...'>{{ choice }}</div>
</label>
{% endfor %}
...which raises the error 'invalid choice'.
Is there anything I'm doing wrong here? Thanks
It's not clear what specifically you are trying to render in your two divs, but in any case you are missing a value attribute on the input that you are rendering manually. Rendering {{ choice }} will also render a second input which will further complicate things.
Have a look at the documentation for how to control rendering of choices - I think you need to do something along these lines:
{% for choice in acc.field1 %}
<label>
<!-- Render the input itself -->
<div class='...'>{{ choice.tag }}</div>
<!-- Render a label for the input manually -->
<div class='...'>{{ choice.choice_label }}</div>
</label>
{% endfor %}
New to django (and in general python). I'm trying to customize one of my forms a bit and would like to know the best way to do this.
Take for example the following, a form with 3 fields, two fields for weight entry and one for a target date.
class BasicFitnessGoalForm(forms.ModelForm):
class Meta:
model = BasicFitnessGoal
fields = ('currentWeightKg','targetWeightKg','targetDate')
widgets = {
'targetDate': forms.DateInput(attrs={'class':'formdatepicker'}),
}
labels = {
'currentWeightKg' : "Current Weight",
'targetWeightKg' : "Goal Weight",
'targetDate' : 'Goal Date'
}
I want to be able to display this form in my templates using something like goalForm.as_p() which generates html like the following:
<p>
<label></label><input><input>...
What I am looking to do is on the weight fields insert an extra element after the input tags and customize the text and css classes. So I end up with something like this:
<p>
<label>Current Weight</label><input type=Number...><span class="customCss">Kg (or text I enter</span>
</p>
So is there anyway to accomplish this inside the model class? I know I could do this with javascript or by looping through the fields in the template instead of using the form.as_p tag. But I want to reuse this so it's cleanest if I can create a method to output the desired html wherever I want to use it.
I've seen some examples where you can set a method on the form like as_foobar where the self.html_output is returned. But I see that that only allows to specify values like "normal_row", "error_row" etc. I want to code a custom template for this form and return the html from a method on the model that I can then access from a template. (or override the as_p method for this model to return custom html depending on field).
Hopefully that makes sense.
Using a custom widget as suggested in the comment is one option, another is to loop through the fields manually.
<form action="/your-name/" method="post">
{% csrf_token %}
{{ form.non_field_errors }}
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
{% if field.id_label = id_currentWeightKg" %}
<span class="customCss">Kg (or text I enter</span>
{% endif %}
</div>
{% endfor %}
<input type="submit" value="Submit" />
</form>
For additional information refer to the Looping over form fields section in the manual.
before you do this, view the HTML source and confirm that id_currentWeightKg is the correct id for the field in question. If not use replace with the correct one.