WTForms DateField returning None - python

I have a DateField that is part of a WTForm in Flask
from wtforms.fields.html5 import DateField
dob = DateField('Date of Birth', [InputRequired()], format='%m-%d-%Y')
if form.is_submitted():
print(form.dob.data)
HTML Template
{{ form.dob.label }}
<input type="date" id="DateofBirth" name="dob" class="form-control">
When the form is submitted it returns None. All other fields of the form work properly. Any suggestions?

Solved it after a bit. Figured out I needed to remove the format parameter in the DateField object. The corrected field looks like this:
dob = DateField([InputRequired()])
When submitted the form.dob.data now outputs the correct date. This answer on another semi-unrelated question helped me out: https://stackoverflow.com/a/9519493/16549743. I guess HTML5 can't accept different formats as explained in that answer and passing the format parameter was messing things up.

Related

DateField's DateInput widget with 'format="%B"' is not localized/translated

I have an UpdateView and when I visit the edit page, my date field renders the month as non-localized.
In my Form I'm using format("%B %Y") to have the full month + year representation, like so:
class CampaignForm(ModelForm):
date = DateField(widget=DateInput(attrs={"autocomplete": "off"}, format="%B %Y"))
I'm using the form in a basic UpdateView setup, and I render the template...
<div class="form-row">
<div class="form-group flatpickrdatetimeinput col-md-6 mb-0">
{{ form.date|as_crispy_field }}
</div>
</div>
The date representation works in terms of month + year, but I need to localize the month name to the local language.
I cannot find the error, I tried changing the field attribute (localize=True) and also I have played around with the settings (i18n, l10n, etc) with no success.
In fact, the settings have worked out so far. In the same template I am using the 'date' template filter and it translates the month just fine
<h2>{{campaign.date | date:'F Y' }}</h2>
How to make use of the internal django localization inside a form/form-field?
While writing the question, I had the idea to try to set the initial data and localize it there.
It indeed worked but it's a workaround and I still think it should work directly from the form.
If you have a solution please feel free to add it. Or leave a comment if you have experienced the issue and you think this is a bug.
from django.utils import formats
class CampaignUpdateView(CampaignMixin, UpdateView):
def get_initial(self):
initial = super().get_initial()
initial.update({"date": formats.date_format(self.object.date, format="F Y", use_l10n=True)})
return initial

Django crispy forms inline label and input field

I have a Django ModelForm containing 3 fields: name, lastname, email. What I want is to adjust the crispy form to have each field along with it's label in one line, so the form will look like this:
Name: <input name>
Lastname: <input lastname>
Email: <input email>
<button Submit>
I tried with FormHelper and Layout and bootstrap inline attribute but all I achieved was having all elements in the form in one line.
I'm really bad at frontend and I'm stuck.
Assuming your settings.py contains:
CRISPY_TEMPLATE_PACK = 'bootstrap4'
you probably want to override the template bootstrap4/field.html which is the one that places the label and the various possible input field together.
to do so, you can copy the templates/bootstrap4/field.html file from the crispy_form package into your templates/bootstrap4/field.html in your application and have it modified as per your needs. When the template engine will render the form fields, it will find your modified field.html before the one in the original template.
you may also want to be refered to the template pack uni_form which, without css, renders the fields almost the way you want. You can use it as is or get yourself inspired by the way it is structured to modify your own field.html.
I managed to achieve my goal by simply changing {{ my_form|crispy }} to {{ my_form.as_p }} in my html code.

Custom form fields in Django

I know how to create forms in forms.py and views.py using models.py, but to want to create a birthdate field. As everyone knows we can do this birthdate = models.DateTimeField(). However, the issue is the user would have to input their birthdate by typing YYYY-MM-DD-HH-MM-SS.
So does anyone here have an elegant way for users to input their birthday?
Thanks
I believe you are looking for a nicer DateTimeWidget, not the field itself. Secondly, unless it's really necesary, you would more likely use a DateField rather than DateTimeField to record user's birthdate ;)
here's just a one example of custom datetime input widget, but there are more:
https://github.com/asaglimbeni/django-datetime-widget
in the forms, you would do:
birthdate = forms.DateTimeField(widget= ...)
cheers
Of course. Tie a date-picker to the birthday input.
See jQuery UI's datepicker widget.
The approach is:
User picks a date from datepicker widget.
The brithday input get's automatically populated as per the selected date.
After form submission, the value in the input goes to the backend.
UPDATE:
Example:
In your template:
<form action=".">
...
{{ form.birthdate }}
...
</form>
<script>
$(function() {
$( "#id_birthdate" ).datepicker({
changeYear: true,
changeMonth: true,
});
});
</script>
The id of the birthdate input is definitely going to be id_birthdate, because that is how Django assigns ids to form fields, so that is what you need to pass in the script's function.
Also, include jqueryui.js in your project.
You could use models.DateField and custom DATE_INPUT_FORMATS if needed:
https://docs.djangoproject.com/en/dev/ref/settings/#date-input-formats

Pre-populating a BooleanField as checked (WTForms)

For the life of me, I can't figure out how to pre-populate a BooleanField with WTForms. I have a field called "active". It defaults to being not checked, and it's not required. So I set it up like...
class QuestionForm(Form):
question = TextField('Question', [validators.Required()])
slug = TextField('Slug', [validators.Required()])
active = BooleanField('Active')
Then I have an EDIT PAGE where I display a form for the 'question' I want to edit.
{{ form.question.label }}
{{ form.question(value=q.question) }}
{{ form.active(value=q.active) }} Show this question?
If 'active' is True, I'd like the BooleanField (checkbox) to have the 'checked' attribute. And if False, not to. But I can't even figure out how to make the checkbox have a checked state, when rendering the form, let alone the conditional part.
The only way, I've been able to get it to show up checked is if I add default=True when defining the form. But that's not what I need.
I've tried using 'default', 'initial', 'value', 'selected' while rendering the form with no luck. And I've searched the docs and Google. I think I'm missing something! :)
UPDATE
Here's what my view looks like. Maybe it is the problem?
#mod.route('/q/<slug>/edit', methods = ['GET', 'POST'])
def edit(slug):
form = QuestionForm(request.form, csrf_enabled=False)
q = Question.query(Question.slug==slug).get()
if request.method=='POST':
if form.validate_on_submit():
q.question = form.data.get('question')
q.slug = form.data.get('slug')
q.active = form.data.get('active')
q.put()
return redirect('/questions')
return render_template('questions/edit.html', form=form, q=q)
If you have an object you can use it to populate your form like form = QuestionForm(obj=my_obj). If you only want to set the active attribute use form = QuestionForm(active=True).
A BooleanField defined like:
checkbox = BooleanField('title',
default=True,
render_kw ={'checked':''})
snahor's answer helped after much searching (+1). The google seems weak on this question. I found I needed
<div class="form-group">
{{adminForm.is_admin.label}}
{{adminForm.is_admin(checked=True, class_="form-control")}}
</div>
<div class="form-group">
{{adminForm.is_admin.label}}
{{adminForm.is_admin(checked=False, class_="form-control")}}
</div>
which I have utilised as
<div class="form-group">
{{adminForm.is_admin.label}}
{{adminForm.is_admin(checked=user.is_admin, class_="form-control")}}
</div>
To have the default boolean value as True, you need to set the default to "checked"
Basic fields Basic fields generally represent scalar data types with
single values, and refer to a single input from the form.
class wtforms.fields.BooleanField(default field arguments, false_values=None)
Represents an input type="checkbox". Set the
checked-status by using the default-option. Any value for default,
e.g. default="checked" puts checked into the html-element and sets the
data to True
Source
class QuestionForm(Form):
question = TextField('Question', [validators.Required()])
slug = TextField('Slug', [validators.Required()])
active = BooleanField('Active', default="checked")
I had the same problem, and after hours of searching and reading, the solution was very simple.
form = forms.TestForm(request.form)
form.yourbooleanfield.checked = YourVariable
if request.method=="POST" and form.validate():
print(form.yourbooleanfield.data)
In addition to specifying in the template, you can likewise specify in the class definition
class QuestionForm(Form):
question = TextField('Question', [validators.Required()])
slug = TextField('Slug' , [validators.Required()])
activeChecked = BooleanField('Active', default=True )
activeUnChecked = BooleanField('Active', default=False )
None of these solutions worked for me. There seems to be a bug in WTForms that has not been fixed.
Instead, when the route is called I set the value of the Boolean field after I have initialised the form. This works for me
form = GameCreateForm(request.form)
form.allow_comments.data = True
This worked for me
BooleanField(default="checked")
https://wtforms.readthedocs.io/en/2.3.x/fields/
class wtforms.fields.BooleanField(default field arguments, false_values=None)

Django: Disabled Dropdown doesn't send information back to POST

I have a dropdown in a modelform and the user should not be able to change the selected value.
I found that a disabled does exactly do what I need. However there is an oddness to this:
The first time when the form opens (GET) the value is selected and the user can't change the value. which is great:
But as soon as there is a validation error with an unrelated field and the POST sends the user back to the same form, the previous information is lost. The disabled foreignkey-dropdown no longer contains any value and is very irritating.
I did some research and found something on stackoverflow and seems when a foreignkey-dropdown widget is disabled, no data is sent back at all. While the validation can be overriden to not throw any errors for the dropdown field as the third answer here explains. However if ANY OTHER unrelated field throws an error then the data is lost, because the disabled dropdown had never sent any data to POST in first place.
It is a tricky situation.
Is there a way to pass in the data within the view to the request.POST ? or what do you suggest? I could use a readonly instead ofdisabled and that would work, however the dropdown can be changed by the user, which is also irritating.
Any ideas? Many Thanks
edit:
Small correction: The data is not completely lost. Rather the select is set wrongly to the initial dummy value.
<select id="id_form-0-deal_type" name="form-0-deal_type" disabled="disabled">
<option selected="selected" value="">---------</option>
<option value="1">deal 1</option>
<option value="2">deal 2</option>
</select>
UPDATE:
The solution from Francis looks very promising. So I have tried his second suggestion and added a hidden inputfield in the html and pass in the correct value into the POST.
The problem is now how to proceed. I have tried to add the missing entry in the formset's form's querydict like this (in order to set the correct dropdown value)
formset.forms[0].data['form-0-deal_type'] = formset.forms[0].data['form-0-hiddenfield']
But it says This QueryDict instance is immutable
The only other way to do it is setting it through Initials with regular formsets. Unfortunally I am using modelformsets, which doesn't support initials for existing forms.
If there is no other solution, I start refactoring my modelformset into a regular formset. Still open for ideas...
Final Update + Solution:
There is no need to refactor modelformset into regular fomsets. In fact I highly discourage doing that, since it brings other problems with itself. modelformsets handle everything for you and fill the missing parts.
The actual problem is the fact that QueryDict are immutable, but this can be easily solved by copying them:
formset = deal_formset(request.POST, queryset=formset_query)
if formset.is_valid():
pass
else:
new_post = request.POST.copy()
deal_types = dict()
for k,v in new_post.items():
if k.startswith('hidden'):
deal_types[k[7:]]= v
for k,v in deal_types.iteritems():
new_post[k] = v
formset = deal_formset(new_post, queryset=formset_query)
This plus the solution of Francis:
{{ formset.management_form }}
{% for fs in formset %}
{{ fs.id }}
<input type="hidden" name="hidden-{{ fs.prefix }}-deal_type" value="{{fs.deal_type.value}}" />
{{fs.deal_type}}
{% endfor %}
{% endif %}
just works wonders... enjoy :)
Its not a django thing, its an HTML thing. Disabled form elements are not sent by the form.
[The Element] cannot receive user input nor will its value be submitted with the form.
http://www.w3.org/TR/html401/interact/forms.html#h-17.12.1 & http://www.w3schools.com/tags/att_input_disabled.asp
you could use readonly if its on a text/textarea
http://www.w3schools.com/tags/att_input_readonly.asp
something else you could do, is show the value plaintext, and submit it as a hidden field....
{{ form.field_name.label_tag }}
{{ form.field_name.value }}
<input type="hidden" name="field_name" value="{{form.field_name.value}}" />
its not very elegant, but it could get you there.
You could also take it a step further and write some JS that looks for disabled elements and adds an input with that element's name and value after.
some sample JQuery:
//Untested, but you get the gist
$(':disabled').each(
function()
{
$(this).after('<input type="hidden" name="' + $(this).attr('name') + '" value="' + $(this).val() + '" />');
}
);
Well, you could set the element with hidden property in the template, using formsets in the view to build the form:
{{form.field.as_hidden}}
and inside the view, if the problem is the data loss, you could always set an initial value for the field that suits your model structure, since it's a foreign key. Of course, you will have to validate the form before commiting it, and if the form is not valid, you can render it with initial values on the fields that must be always filled.
I think this is a HTML issue rather than Django, disabled form fields don't post their values back so you're losing the value.
Would it be possible to rebind the value to the field if validation fails? You could try something like
if form.is_valid(): # All validation rules pass
#save form, redirect, etc.
else:
form.disabled_field = my_value
return render(request, 'contact.html', {'form': form,})
Obviously you'll need to replace the field name and value with the correct data from your model.

Categories