Django cannot save blank value in FloatField - django

I have a simple model in Django:
class Test(models.Model):
name = models.FloatField(default=0.0, blank=True)
In my views.py, I am fetching user input from a page. The html code is:
<form action="{% url 'test' %}" method="POST">
{% csrf_token %}
<input type="number" placeholder="Test" class="form-control mb-2" name="test">
<input type="submit" class="btn btn-primary" value="Submit">
</form>
The views.py code is:
name = request.POST.get('test', '0.0') #I have tried without the '0.0' as well.
new_test = Test(name=name)
new_test.save()
I keep getting the error:
ValueError at /test
Field 'name' expected a number but got ''.
How can I make django save a blank value or '0.0'(default value) or a null value when the user enters nothing. It seems to accept none of these.

Your field name is a FloatField hence it expects numbers, hence when an empty string ('') is passed to it you get an error. request.POST.get('test', '0.0') does not work because when the user submits the field leaving it blank the fields value is kept as an empty string, meaning the key is present in request.POST but it's value is ''.
Normally if one would have used a Form class [Django docs] or a ModelForm class, these values would have been cleaned automatically by the form and would have been replaced by None. I would advice you to use the form classes. If you still insist on doing things manually you would go about cleaning the value somewhat like this:
from django.core import validators
name = request.POST.get('test', '0.0')
if name in validators.EMPTY_VALUES: # if name in (None, '', [], (), {})
name = None
else:
name = float(name) # Might still get error here, if receiving non-numeric input
new_test = Test(name=name)
new_test.save()
Note: This is just a very small part of cleaning and validation, there are more errors that can occur here, example a user enters a non-numeric input into the field, etc. I would advice using form classes.

You have to use something like this:
name = models.FloatField(null=True, blank=True, default=0.0)

Related

Django update boolean field with a form

My simple web-application has two models that are linked (one to many).
The first model (Newplate) has a boolean field called plate_complete. This is set to False (0) at the start.
questions:
In a html page, I am trying to build a form and button that when pressed sets the above field to True. At the moment when I click the button the page refreshes but there is no change to the database (plate_complete is still False). How do I do this?
Ideally, once the button is pressed I would also like to re-direct the user to another webpage (readplates.html). This webpage does not require the pk field (but the form does to change the specific record) Hence for now I am just refreshing the extendingplates.html file. How do I do this too ?
My code:
"""Model"""
class NewPlate(models.Model):
plate_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=200)
created_date = models.DateTimeField(default=timezone.now)
plate_complete = models.BooleanField()
"""view"""
def publish_plates(request,plate_id):
newplate = get_object_or_404(NewPlate, pk=plate_id)
newplate.plate_complete = True
newplate.save()
#2nd method
NewPlate.objects.filter(pk=plate_id).update(plate_complete = True)
return HttpResponseRedirect(reverse('tablet:extendplates', args=[plate_id]))
"""URLS"""
path('readplates', views.read_plates, name='readplates'),
path('extendplates/<pk>/', views.show_plates, name='showplates'),
path('extendplates/<pk>/', views.publish_plates, name='publishplates'),
"""HTML"""
<form method="POST" action="{% url 'tablet:publishplates' newplate.plate_id %}">
{% csrf_token %}
<button type="submit" class="button" value='True'>Publish</button></form>
-------Added show plates view:---------
def show_plates(request,pk):
mod = NewPlate.objects.all()
newplate= get_object_or_404(mod, pk=pk)
add2plate= Add2Plate.objects.filter(Add2Plateid=pk)
return render(request, 'tablet/show_plates.html', {'newplate': newplate,'add2plate': add2plate})
Thank you
The problem is two of your urls have the same pattern 'extendplates/<pk>/'. Django uses the first pattern that matches a url. I suppose that one of these view views.show_plates is meant to display the form and the other views.publish_plates is meant to accept the posted form data.
This means that simply both of these views should simply be a single view (to differentiate if the form is submitted we will simply check the requests method):
from django.shortcuts import redirect, render
def show_plates(request, plate_id):
newplate = get_object_or_404(NewPlate, pk=plate_id)
if request.method == "POST":
newplate.plate_complete = True
newplate.save()
return redirect('tablet:extendplates', plate_id)
context = {'newplate': newplate}
return render(request, 'your_template_name.html', context)
Now your url patterns can simply be (Note: Also captured arguments are passed as keyword arguments to the view so they should be consistent for your view and pattern):
urlpatterns = [
...
path('readplates', views.read_plates, name='readplates'),
path('extendplates/<uuid:plate_id>/', views.show_plates, name='showplates'),
...
]
In your form simply forego the action attribute as it is on the same page:
<form method="POST">
{% csrf_token %}
<button type="submit" class="button" value='True'>Publish</button>
</form>
You should avoid changing state on a get request like your view does currently.
Handle the POST request and change the data if the request is valid (ensuring CSRF protection).
def publish_plates(request,plate_id):
newplate = get_object_or_404(NewPlate, pk=plate_id)
if request.method == "POST":
newplate.plate_complete = True
newplate.save(update_fields=['plate_complete']) # a more efficient save
#2nd method
NewPlate.objects.filter(pk=plate_id).update(plate_complete=True)
return HttpResponseRedirect(reverse('tablet:extendplates', args=[plate_id]))
You could also put a hidden input in the form, or make a form in Django to hold the hidden input, which stores the plate_id value and that way you can have a generic URL which will fetch that ID from the POST data.
Now the real problem you've got here, is that you've got 2 URLs which are the same, but with 2 different views.
I'd suggest you change that so that URLs are unique;
path('extendplates/<pk>/', views.show_plates, name='showplates'),
path('publish-plates/<pk>/', views.publish_plates, name='publishplates'),

Django TypeError: __init__() got multiple values for keyword argument 'required'

it is probably too late again for me to find out what is wrong. I am having a simple form forms.py:
class ImportPortfolioForm(forms.Form):
file = forms.FileField(required=True)
price_per_share = forms.BooleanField('Price per Share', required=False, initial=True,
help_text="If not checked, total cost is expected in Price column.")
this is html:
<form method="post" action="" class="wide" class="form-horizontal" enctype="multipart/form-data">
<div class="col-md-6">
{% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="btn btn-primary">Import</button>
</div>
</form>
and this is views.py:
if request.method == 'POST':
form = ImportPortfolioForm(request.POST, request.FILES)
if form.is_valid():
data = form.cleaned_data
# work with file ...
else:
form = ImportPortfolioForm()
I am getting error if I try to load form url:
TypeError: __init__() got multiple values for keyword argument 'required'
If I remove required like this:
class ImportPortfolioForm(forms.Form):
file = forms.FileField(required=True)
price_per_share = forms.BooleanField('Price per Share', initial=True,
help_text="If not checked, total cost is expected in Price column.")
I can load form url. If I add file and send form, it claims field price per share is required:
I do not know why this behaviour happens. I guess request.POST in form initialization somehow adds required=True to the form. But I do not know why it does so or why I can not overwrite it in a form. Any ideas?
...
price_per_share = forms.BooleanField('Price per Share', required=False, initial=True)
Only model fields accept a label as the first positional argument. Form fields require you to use the label keyword. required is the first argument for form fields, so you're passing it both as a positional argument and as a keyword argument.
Generally you'd only use keyword arguments in form fields. The keyword you're probably looking for is label:
price_per_share = forms.BooleanField(label='Price per Share', required=False, initial=True)

Django form and field validation does not work?

When using the Django built-in Form classes the validation routine does not seem to work.
The form consists simply of firstname and lastname. Firstname is required, and for testing purposes I check if lastname is Smith and raise an exception. When I violate those requirements nothing happens, i.e. no exception is being raised - after submitting the form the defined action (POST to union/VIP_best/) is simply triggered without any validation. The form is called at union/contact/, directed from urls.py to views.ContactView.as_view()
Here is my setup so far:
views.py
from union.forms import ContactForm
class ContactView(generic.edit.FormView):
template_name = 'union/contact.html'
form_class = ContactForm
forms.py
from django import forms
from django.core.exceptions import ValidationError
class ContactForm(forms.Form):
firstname = forms.CharField(label='Vorname', max_length=20, required=True, error_messages={'required': 'Please enter first name!'})
lastname = forms.CharField(label='Nachname', max_length=20)
def clean_lastname(self):
data = self.cleaned_data['lastname']
if data != "Smith":
raise forms.ValidationError("Your last name is not Smith.")
else:
raise forms.ValidationError("Your last name is Smith.")
return data
templates/union/contact.html
<form action="/union/VIP_best/" method="post">
{% csrf_token %}
{{ form.as_table }}
<tr>
<td><input type="submit" value="Submit"></td>
</tr>
</form>
What am i missing so that the .clean() or .is_valid is being triggered?
Do I have to explicitly call Field.clean() and is_valid()? If so, where?
The tutorials and Django documentation do not seem to mention anything the like.
Django 1.7, Python 3.4.2
Your form should be submitting to itself. Meaning, if the form is on /union/contact/, you should be POSTing to /union/contact/. Currently, you have it submitting to a different view/url.
The problem is arising because your form processing is happening in the FormView, not at your success_url(). You need to POST to the view that is actually responsible for the validation of the form (in this case, your ContactView at /union/contact/).
As a side note, it would probably be better to modify the action of the form to use {% url 'your_form_url_name' %}, as opposed to hard-coding the url into the template.

Populate a PasswordField in wtforms

Is it possible to populate a password field in wtforms in flask?
I've tried this:
capform = RECAPTCHA_Form()
capform.username.data = username
capform.password.data = password
The form is defined like:
class RECAPTCHA_Form(Form):
username = TextField('username', validators=[DataRequired()])
password = PasswordField('password', validators=[DataRequired()])
remember_me = BooleanField('Remember me.')
recaptcha = RecaptchaField()
The template looks like this:
<form method="POST" action="">
{{ form.hidden_tag() }}
{{ form.username(size=20) }}
{{ form.password(size=20) }}
{% for error in form.recaptcha.errors %}
<p>{{ error }}</p>
{% endfor %}
{{ form.recaptcha }}
<input type="submit" value="Go">
</form>
I have tried to change the PasswordField to a TextField, and then it works.
Is there some special limitation to populating PasswordFields in wtforms?
Update: After looking through the WTForms docs I found an even better solution. There is a widget arg.
from wtforms import StringField
from wtforms.widgets import PasswordInput
class MyForm(Form):
# ...
password = StringField('Password', widget=PasswordInput(hide_value=False))
As yuji-tomita-tomita pointed out, the PasswordInput class (source) has an hide_value argument, however the constructor of PasswordField (source) does not forward it to the PasswordInput. Here is a PasswordField class that initializes PasswordInput with hide_value=False:
from wtforms import widgets
from wtforms.fields.core import StringField
class PasswordField(StringField):
"""
Original source: https://github.com/wtforms/wtforms/blob/2.0.2/wtforms/fields/simple.py#L35-L42
A StringField, except renders an ``<input type="password">``.
Also, whatever value is accepted by this field is not rendered back
to the browser like normal fields.
"""
widget = widgets.PasswordInput(hide_value=False)
Something I've found with Flask, and Flask apps in general, is that the source is the documentation. Indeed, it looks like by default you cannot populate the field. You can pass an argument hide_value to prevent this behavior.
This is a good call, since if you can populate the field, you have access to the raw password... which could be dangerous.
class PasswordInput(Input):
"""
Render a password input.
For security purposes, this field will not reproduce the value on a form
submit by default. To have the value filled in, set `hide_value` to
`False`.
"""
input_type = 'password'
def __init__(self, hide_value=True):
self.hide_value = hide_value
def __call__(self, field, **kwargs):
if self.hide_value:
kwargs['value'] = ''
return super(
I believe there is an easier way to access the data of the password field, without usinghide_value. In your view, simply add in the request data as an argument to the form's constructor:
from flask import request
capform = RECAPTCHA_Form(request.form)
capform.username.data = username
capform.password.data = password
This should make the password input available for validation, and to be used in testing if desired.

Styling forms and django form values

I'm trying to style a form with CSS. First of all I haven't seen a complete example, other than on the official documentation, so I would appreciate any blog entries, or articles.
Now on my form, a typical Charfield gets translated on html like this:
<input type="text" name="artists" id="id_artists" />
If my form contains errors on some field, the previous Charfield remembers the value and goes:
<input type="text" name="artists" value="Pink Floyd" id="id_artists" />
How can I get this value (value="Pink Floyd") in django forms? Say my field is {{form.artists}}, I can use {{form.artists}} as the field, {{form.artists.label}} as label, {{form.artists.errors}} and {{form.artists.help_text}}, but how about this value?
Thanks in advance!
Create the input field specifically, rather than relying on django to auto-create it.
Something like:
<input type="text" name="artists" id="id_artists" value="{{form.artists.title}}" />
should work
You can get the current value of the field from the data property:
{{ form.artists.data }}
I can't see the Django documentation mentioning this, but it works...
You can also add additional validation to the form class to do something with the form data. From djangobook.com:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
email = forms.EmailField(required=False)
message = forms.CharField(widget=forms.Textarea)
def clean_message(self):
message = self.cleaned_data['message']
num_words = len(message.split())
if num_words < 4:
raise forms.ValidationError("Not enough words!")
return message
Inside of the clean_message method you can access a given field using the self.cleaned_data dictionary. This dictionary is available for any form that is validated.
Be sure to return the field, in this case message, or else None is returned.