flaskform pass a variable (WTForms) - python

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()

Related

How does StringField in WTForms Create Fields in HTML File

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

Passing a variable into a WTForms class

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)

Flask - WTForm - Using Query results as RadioField Choices

Referencing the following Form.
CONTROL_CHOICES = Session.query(schema.OfficeType).order_by(schema.OfficeType.descr).all()
CONTROL_CHOICES = [(office.id, office.descr) for office in CONTROL_CHOICES]
class ControlForm(Form):
institution = RadioField('Institution', choices=CONTROL_CHOICES)
date = DateField('Date')
submit = SubmitField('SUBMIT')
Simple HTML
<form action="composition_profile" method="get">
{{control_form.hidden_tag()}}
{{control_form.institution.label}}
{{control_form.institution}}
{{control_form.date.label}}
{{control_form.date}}
{{control_form.submit}}
</form>
The radio fields are successfully printed.
However, when filling out the form, validate_on_submit() prints no errors, but does not execute code within the if
validate() prints the following error.
{'institution': ['Not a valid choice']}
#app.route('/composition_profile', methods=['GET', 'POST'])
def composition_profiles():
if request.method == 'GET':
if request.args.get('institution') and request.args.get('date'):
form = ControlForm(request.args)
print(form.institution.data)
if form.validate():
print('terms')
print(form.errors)
Any idea how it is resulting in an improper choice? Im not really sure what's going on. Changing it to QuerySelectField works, but I'd like the radio functionality
Consider re-factoring:
def my_view():
class F(MyBaseForm):
pass
F.username = TextField('username')
for name in iterate_some_model_dynamically():
setattr(F, name, TextField(name.title()))
form = F(request.POST, ...)
# do view stuff
Source: http://wtforms.simplecodes.com/docs/1.0.1/specific_problems.html#dynamic-form-composition
The Issue appears to have been caused due to the fact that WTForm expects the Value field, to be a String as opposed to an Integer
Changing
CONTROL_CHOICES = [(office.id, office.descr) for office in CONTROL_CHOICES]
To
CONTROL_CHOICES = [(str(office.id), office.descr) for office in CONTROL_CHOICES]
Alleviates the issue

How to create a very simple search view for one column of mysql data in Django?

I have searched around and see that most are pointing to a search that was created by Julien Phalip: http://julienphalip.com/post/2825034077/adding-search-to-a-django-site-in-a-snap
Also the answer seems to be here: Very simple user input in django
However I am very new to Django and wanted to create a view where I actually understand what is happening so I have been going through the official Django and the Tango with Rango tutorials but I do not see a straightforward example of what I am trying to understand in regards to a simple form search. The main question I have is why is POST used in the example instead of GET? I thought POST was used to "create" data entries in mysql whereas GET is used to lookup/search for data entries? Am I missing something fundamental about using one vs the other?
I have the following simple example from my app:
models.py
class hardware(models.Model):
text = models.CharField(max_length=200, unique=TRUE)
pub_date = models.DateTimeField(default=timezone.now)
def __unicode__(self):
return self.text
class Barcode(models.Model):
hardware = models.ForeignKey(Hardware)
text = models.CharField(max_length=50)
pub_date = models.DateTimeField(default=timezone.now)
def __unicode__(self):
return self.text
forms.py
class HardwareForm(forms.modelForm):
class Meta:
model = Hardware
fields = ['text'}
views.py
def hardware_search(request):
if request.method == 'POST':
search_id = request.POST.get('textfield', None)
try:
hardwarename = Hardware.objects.get(text = search_id)
html = ("<H1>%s</H1>", hardwarename)
return HttpResponse(html)
except Hardware.DoesNotExist:
return HttpResponse("no such hardware found")
else:
return render(request, 'search.html')
search.html
<form method="POST" action="/hardware_search.html">
{% csrf_token %}
<input type="text" name="textfield">
<button type="submit">Upload text</button>
</form>
My questions are is this the most simple way to request user input to search for and generate the search results? Why is POST used? I plugged in this code and it does seem to work but i just can't understand why.
Secondly how can I display asssociated foreignkey class along with the main class 'hardware' search results? Does the ForeignKey association give a shortcut way of displaying that data as well?
thanks!
The W3 has an excellent introduction to POST vs GET here. There is a lot to be said for why someone might use POST or GET, and what their roles should be. You are probably more interested in the differences from the user's (browser's) perspective. The biggest differences between using POST and GET in a browser, is that the GET request will display the parameters in the URL. Change your form to GET to see for yourself. The user will be taken to:
/hardware_search.html?textfield=Upload%20text
As opposed to where they are taken to when the form action is POST:
/hardware_search.html
The value of the textfield field is still sent to the server, but is not visible in the URL.
There are quite a few other differences in the behavior of GET and POST in form submission. I would highly recommend reading over that introduction by the W3.
You're right, POST is not really appropriate for a search form. Using GET here would be better.
The other thing wrong is that there's no need at all for a ModelForm, or really for any kind of Django form. You're not doing any validation, you're not even using the form for output, so it would be better to leave that out altogether. That makes the view look like this:
def hardware_search(request):
query = request.GET.get('textfield', None)
if query:
try:
hardwarename = Hardware.objects.get(text = query)
html = ("<H1>%s</H1>", hardwarename)
return HttpResponse(html)
except Hardware.DoesNotExist:
return HttpResponse("no such hardware found")
else:
return render(request, 'search.html')
and you can change the form action to GET.

How do I associate input to a Form with a Model in Django?

In Django, how do I associate a Form with a Model so that data entered into the form are inserted into the database table associated with the Model? How do I save that user input to that database table?
For example:
class PhoneNumber(models.Model):
FirstName = models.CharField(max_length=30)
LastName = models.CharField(max_length=30)
PhoneNumber = models.CharField(max_length=20)
class PhoneNumber(forms.Form):
FirstName = forms.CharField(max_length=30)
LastName = forms.CharField(max_length=30)
PhoneNumber = forms.CharField(max_length=20)
I know there is a class for creating a form from the the model, but even there I'm unclear on how the data actually gets to the database. And I'd like to understand the inner workings before I move on to the time-savers. If there is a simple example of how this works in the docs, I've missed it.
Thanks.
UPDATED:
To be clear -- I do know about the ModelForm tool, I'm trying to figure out how to do this without that -- in part so I can better understand what it's doing in the first place.
ANSWERED:
With the help of the anwers, I arrived at this solution:
Form definition:
class ThisForm(forms.Form)
[various Field assignments]
model = ThisModel()
Code in views to save entered data to database:
if request_method == 'POST':
form = ThisForm(request.POST)
if form.is_valid():
for key, value in form.cleaned_data.items():
setattr(form.model, key, value)
form.model.save(form.model)
After this the data entered in the browser form was in the database table.
Note that the call of the model's save() method required passage of the model itself as an argument. I have no idea why.
CAVEAT: I'm a newbie. This succeeded in getting data from a browser to a database table, but God only knows what I've neglected or missed or outright broken along the way. ModelForm definitely seems like a much cleaner solution.
Back when I first used Forms and Models (without using ModelForm), what I remember doing was checking if the form was valid, which would set your cleaned data, manually moving the data from the form to the model (or whatever other processing you want to do), and then saving the model. As you can tell, this was extremely tedious when your form exactly (or even closely) matches your model. By using the ModelForm (since you said you weren't quite sure how it worked), when you save the ModelForm, it instantiates an object with the form data according to the model spec and then saves that model for you. So all-in-all, the flow of data goes from the HTML form, to the Django Form, to the Django Model, to the DB.
Some actual code for your questions:
To get the browser form data into the form object:
if request.method == 'POST':
form = SomeForm(request.POST)
if form.is_valid():
model.attr = form.cleaned_data['attr']
model.attr2 = form.cleaned_data['attr2']
model.save()
else:
form = SomeForm()
return render_to_response('page.html', {'form': form, })
In the template page you can do things like this with the form:
<form method="POST">
{{ form.as_p }}
<input type="submit"/>
</form>
That's just one example that I pulled from here.
I'm not sure which class do you mean. I know that there were a helper, something like form_for_model (don't really remember the exact name; that was way before 1.0 version was released). Right now I'd it that way:
import myproject.myapp.models as models
class PhoneNumberForm(forms.ModelForm):
class Meta:
model = models.PhoneNumber
To see the metaclass magic behind, you'd have to look into the code as there is a lot to explain :]. The constructor of the form can take instance argument. Passing it will make the form operate on an existing record rather than creating a new one. More info here.
I think ModelForm.save documentation should explain it. With its base class (Form) you would need to use the Form.cleaned_data() to get the field values and set them to appropriate Model fields "by hand". ModelForm does all that for you.
The Django documentation is pretty clear on this subject. However, here is a rough guide for you to get started: You can either override the form's save method or implement that functionality in the view.
if form.is_valid() # validation - first the fields, then the form itself is validated
form.save()
inside the form:
def save(self, *args, **kwargs):
foo = Foo()
foo.somefield = self.cleaned_data['somefield']
foo.otherfield = self.cleaned_data['otherfield']
...
return foo.save()

Categories

Resources