I'm relatively inexperienced with Python and Flask, and am stuck trying to pass a variable to a WTForms class.
Here's what I have:
views.py
#app.route('/teacher/tasks/new')
#login_required
def new_hw_task():
classes = Class.query.filter_by(userid=current_user.userid).all()
form = NewTaskForm(classes = classes)
return render_template('/teacher/new_hw_task.html', form=form)
forms.py
class NewTaskForm(FlaskForm):
classes = SelectMultipleField('Select classes to assign this homework to', choices = [("1", "Class 1"), ("2","Class 2")])
new_hw_task.html
<div class="form-group">
{{ form.classes.label }}
{{ form.classes(class_="form-control selectpicker", placeholder=form.classes.description, title="Select at least one class to assign", show_tick=true)}}
</div>
I want the classes variable (an instance of a Class class defined in models.py - yes, yes, I know how sloppy it is to have a class called 'Class') to be accessible in forms.py so that I can replace the choices in the SelectMultipleField with ones from classes. However, I can't find a way to pass it through (you can see that I've tried putting classes=classes into the parentheses after NewTaskForm).
Actually, my preferred way to do this would be to simply access current_user (the session-based object set by flask_login) from within forms.py, but I appear to be unable to do that either, even if I import current_user at the top of the file.
Is anybody able to explain to me where I'm going wrong here, please?
The WTForms documentation for SelectField explains how to pass variables into a form from a view. It's as simple as assigning a list of choices to form.field.choices. In turn, you remove the choices= keyword argument from the field constructor.
Adapted for your case, it would look like this.
#app.route('/teacher/tasks/new')
#login_required
def new_hw_task():
classes = Class.query.filter_by(userid=current_user.userid).all()
form = NewTaskForm()
form.classes.choices = classes
return render_template('/teacher/new_hw_task.html', form=form)
Related
I'm a beginner in coding and currently learning Python. I have been using Flask and WTForms recently to create a registration form. Besides just following tutorials to build the form, I'm also trying to understand it a little bit and see how it works (just generally). So my question is about the StringField.
I have created an html file called registr.html and it will be rendered. Inside of this html file, there is two lines of code that will create a field for the user to input their username, see below:
{{ form.username.label }}
{{ form.username }}
And in my Python application, I have created something below, say Section A:
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
And below, say Section B:
#app.route("/register", methods=['GET', 'POST'])
def register():
form = RegistrationForm()
return render_template('register.html', title='Register', form=form)
Here is my question. I understand form.username.label will put the label "Username" above the input field so people know this is where they input their username. And then I think what form.username does is actually creating a field so people can enter something in there. So my understanding is that username is an attribute that is equal to StringField('Username', validators=[DataRequired()]), which is an object of class StringField, and since form is equal to RegistrationForm(), So executing form.username in the html file is like executing Registration.StringField('Username', validators=[DataRequired()]). Am I correct, here? If so, how executing an object StringField('Username', validators=[DataRequired()]) can create an input field in the html? Can we execute an object? I think we can only execute a method in a class, correct? So there must be something else going on that I don't understand. Again I'm still a beginner and learning Python. So any help is be greatly appreciated!
there is a default render widget. You can overwrite the widget if you need custom HTML to be rendered (https://wtforms.readthedocs.io/en/stable/widgets.html). The specific the render widget used is here:
class Input(object):
"""
Render a basic ``<input>`` field.
This is used as the basis for most of the other input fields.
By default, the `_value()` method will be called upon the associated field
to provide the ``value=`` HTML attribute.
"""
html_params = staticmethod(html_params)
def __init__(self, input_type=None):
if input_type is not None:
self.input_type = input_type
def __call__(self, field, **kwargs):
kwargs.setdefault("id", field.id)
kwargs.setdefault("type", self.input_type)
if "value" not in kwargs:
kwargs["value"] = field._value()
if "required" not in kwargs and "required" in getattr(field, "flags", []):
kwargs["required"] = True
return Markup("<input %s>" % self.html_params(name=field.name, **kwargs))
I want to pass a str to be used as the prompt for a form. I thought it would be simple but it is proving to be difficult.
Here is my code:
class PostForm(FlaskForm):
post = TextAreaField(Question, validators=[DataRequired()])
submit = SubmitField('Submit')`
And,
form = PostForm('my question')
the corresponding html
{{ wtf.quick_form(form) }}
So, I still don't have an answer to the question, but I did manage to come up with a solution.
class PostForm(FlaskForm):
post = TextAreaField(_l('This no longer matters'), validators=[DataRequired()])
submit = SubmitField(_l('Submit'))
And then in the routes
from wtforms.fields.core import Label
form = PostForm()
form.post.label = Label("post", 'Real question goes here')}
The explanation for this is that TextAreaField creates a label attribute on post that is accessible and changable, but it needs to be formated correctly as a Label object from wtforms.fields.core. (Simply reassigning it as a string did not work). The representation of this object is:
<label for="post">Real question goes here</label>
And it is of type
<class 'wtforms.fields.core.Label'>
Today I'm figured out about a similar problem as yours. I wanted to pass a variable to FlaskForm. For a small CV creation app, I want to give the user an opportunity to create more than 1 entry for his work experience and I wanted to do it with FieldList and FormField. And I needed to do it on one page so in one form.
My solution is pretty simple python implementation of factory pattern for forms:
class ConstructorForm(FlaskForm):
...
work_experience_form = FieldList(FormField(WorkExperienceForm), min_entries=1, max_entries=1)
skills_form = FieldList(FormField(SkillsForm), min_entries=1, max_entries=1)
...
And here is my function for building extending forms:
def constructor_form(work_experience_forms=1, skills_forms=1):
class _ConstructorForm(ConstructorForm):
pass
_ConstructorForm.work_experience_form = FieldList(
FormField(WorkExperienceForm), min_entries=work_experience_forms, max_entries=work_experience_forms
)
_ConstructorForm.skills_form = FieldList(
FormField(SkillsForm), min_entries=skills_forms, max_entries=skills_forms
)
return _ConstructorForm()
Try this:
def PostForm(question)
class F(Flaskform):
post = TextAreaField(question, validators=[DataRequired()])
submit = SubmitField('Submit')
return F()
I need a form that has a dynamically created part and a static part to it. So I'm thinking of subclassing ... but can't figure out how this would work with a dynamically created form.
I am creating my dynamic form in this way:
from views.py
def create_legumes_form_class(legumes):
form_fields={}
for legume in legumes:
field_id = 's_{}'.format(legume.id)
form_fields[field_id] = IntegerField(default=0 , validators = [InputRequired()])
return type('OrderForm', (Form,), form_fields)
Then instantiating the form in my view function :
legumes = Legumes.query.filter_by(disponible = True).all()
OrderForm = create_legumes_form_class(legumes)
form = OrderForm()
But to this OrderForm(), I need to add a couple BooleanField(s) that will always be the same (ie. not dynamically created from the Query).
So I am trying to add a (static) form in my forms.py and instantiate it like this in the view function:
form=OrderForm(StaticForm)
But I'm getting
TypeError: formdata should be a multidict-type wrapper that supports the 'getlist' method
And I don't know where to take it from here !
What am I doing wrong/not doing ?
You can't pass another class to the OrderForm constructor and have it subclass that class. You can, however, provide it when you call type.
return type('OrderForm', (Form, StaticForm), form_fields)
If StaticForm subclasses Form this can be further simplified.
return type('OrderForm', (StaticForm,), form_fields)
I have a wtforms form
class MyForm(Form):
names = SelectField('name', choices=[])
The choices of names field is built dynamically and rendered in the template by an ajax call. When I submit the form, it raises an exception "not a valid choice". I don't want the form to validate the choices of names field for me. How can I disable the validation?
I did something like this to step around the SelectMultipleField validation in WTForms. It should work the same way with a plain SelectField
class NonValidatingSelectMultipleField(SelectMultipleField):
"""
Attempt to make an open ended select multiple field that can accept dynamic
choices added by the browser.
"""
def pre_validate(self, form):
pass
I simply override the built-in validation.
I was stuck with the same issue. The solution provided by Xealot is great. I found that there is an option to set validation to False using validate_choice=False. I have included an example of both the solutions below.
class NonValidatingSelectField(SelectField):
"""
Attempt to make an open ended select multiple field that can accept dynamic
choices added by the browser.
"""
def pre_validate(self, form):
pass
class MyForm(Form):
names = NonValidatingSelectField('name')
names2 = SelectField('name2', validate_choice=False)
By "I don't want the form to validate the choices", I assume you actually mean "I'm going to do it myself later and doubt the form's ability to do it correctly".
But you are in luck! You can subclass an existing form to add choices dynamically.
class MyForm(Form):
# other fields...
def some_handler(request):
name_choices = build_name_choices()
class RealForm(MyForm):
names = SelectField('name', choices=name_choices)
form = RealForm(request.GET)
form.validate()
This also saves you from the tedium of merging the form's validation and error messages with those you generate yourself later.
I am trying to add a special template property to one of the form fields which I will than use to render the form in template tag. Here is the code:
form = ProfileForm()
for field in form:
if field.name == 'email':
field.template = 'email_field.html'
This way, original form variable is not modified. Is there a way to achive my goal?
I'm going to assume you might want to build a html5 email field:
from django import forms
from django.forms.widgets import Widget
from django.utils.safestring import mark_safe
class Html5Email(Widget):
def render(self, name, value, attrs=None):
return mark_safe(u'<input name="custom-email" type="email" />')
class YourForm(forms.Form):
html5_email = forms.CharField(widget=Html5Email())
I came up with the above by glancing at the Django source code. Since I haven't personally use the above in an actual project, the code will probably need to be fleshed out.