WTForm validator not giving feedback on non-numeric values - flask

I have a wtform and want to give feedback to users if they enter a non numeric value. I am using
availablequantity = IntegerField('Available quantity',
validators=[ NumberRange(min=1, max=100), DataRequired()])
and on html side I have,
<div class="form-group">
{% if form.availablequantity.errors %}
{{ form.availablequantity(class="form-control is-invalid") }}
<div class="invalid-feedback">
{% for error in form.availablequantity.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.availablequantity(class="form-control", placeholder="Available quantity") }}
{% endif %}
</div>
now if user enters a numeric value things are fine but if they enter non-numeric values, I don't get any useful feedback (I was expecting something like, "you must enter a numeric value between 1 and 100")

Add the following in your request (not in the form class:
form.availablequantity.errors.append("Enter num between 1 and 100")
or add message argument
NumberRange(min=1, max=100, message='Num must be between 1 and 100')

Related

What does this html tag mean in django's example?

This is django's polls demo, and most are well documented. However, in this part:
https://docs.djangoproject.com/en/3.0/intro/tutorial04/
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
Vote again?
The documentation doesn't say anything about this part:
vote{{ choice.votes|pluralize }}
And from the generated html page, I can't see what's the role of this piece?
pluralize is an in-built Django template tag that attempts to convert the word that it is appended to to plural. So you feed it a number, and if the number is 1 then it returns '', but if the number is greater than 1, it returns 's'.
https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#pluralize

How to set a variable value in a Flask HTML template

I am trying to a assign a value to a WTForm field within the HTML template. I need to do it this way as the value assigned will depend on what iteration of the for loop it is in. Any other way of passing a value back to the python script will work as well.
I am currently getting an error. I am trying to assign the record field of the form equal to the id of the record it is currently viewing.
Below is the code:
<div class="modal-body">
<p>Name of Training: {{ record.training_name }}</p>
<p>Name of Provider: {{ record.training_provider }}</p>
<p>Date of Training: {{ record.date_submitted.strftime('%d/%m/%Y') }}</p>
<p>CPD Hours: {{ record.cpd }}</p>
<p>Certificate: View</p>
<form action="" method="post">
{{ form.hidden_tag() }}
<fieldset>
<div class="form-group">
{{ form.reason(class="form-control", placeholder="Reason for Denial (if applicable)") }}
{% set form.record.data=record.id %}
</div>
</fieldset>
When attempting to use the solution above I am getting the following error: jinja2.exceptions.TemplateSyntaxError: expected token 'end of statement block', got '.'
If I'm understanding you correctly:
{{ form.record(value=record.id, class="d-none") }}
instead of
{% set form.record.data=record.id %}

Django iteration (for ... in ...) how to put it in different divs?

I have few instances of model.
my model:
class Record(models.Model):
name = models.ForeignKey(Car)
image = models.ImageField(upload_to='images/')
created = models.DateTimeField(
default=timezone.now)
view:
def allrecords(request):
records = Record.objects.all().order_by('created')
return render(request, 'mycar/allrecords.html', {'records' : records})
I want show it on my website. In my template i have:
{% for record in records %}
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endfor %}
Now i get list of my records, but i would like put the newest record to first div, next to second etc. How can i do that?
I show simple screen how i would like have that (if someone will create new record, it will go to first div and other records will change place. Is any possibility to do something like that?
edit:
<div>
{% for record in records %}
{% if forloop.counter == 1 %}
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
{% endfor %}
</div>
<div>
{% for record in records %}
{% if forloop.counter == 2 %}
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
{% endfor %}
</div>
.
.
# till your 5th image
You can use forloop.counter to get the iteration number and check what is the iteration the loop and handle data accordingly.
In addition you can use CSS to make the layout work as you want.
Here is the information for Django template counter
Edit :
{% for record in records %}
<div>
{% if forloop.counter == 1 %}
# Here you can get your first images
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
</div>
<div>
{% if forloop.counter == 2 %}
# Here you can get your first images
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
</div>
.
.
# till your 5th image
{% endfor %}
There are two ways to do this. If you want to set this option for a single view then:
def all_records(request):
records = Record.objects.all().order_by('-created')
return render(request, 'mycar/allrecords.html', {'records' : records})
You're almost correct but order_by('created') leads to asceding order while order_by('-created') leads to descending order which is what you require.
Alternatively, if you want to have this setting to apply to all views then set class Meta in your models.py which will ensure that wherever you use Record.objects.all() it returns Records in descending order of created field:
class Record(models.Model):
name = models.ForeignKey(Car)
image = models.ImageField(upload_to='images/')
created = models.DateTimeField(
default=timezone.now)
class Meta:
ordering = ('-created')
It's Django design pattern to make all logical decisions in models and views and only just plugin formatted data in templates. You shouldn't add any complex logic in templates.
I'm assuming the question means that the model might have more than 5 records. If so, a more generic solution would be
<div class='row'>
<div class='firstimage'>
<img src={{ records[0].image.url}}/>
{{record.name}}
</div>
{% for record in records %}
{% if forloop.counter > 1 %}
<div class='subsequentimage'>
<img src={{ record.image.url}}/>
{{record.name}}
</div>
{% endif %}
{% cycle "" "</div><div class='row'>" "" %}
{% endfor %}
</div>
Note the use of the 'cycle' tag to begin a new row div every third cell div.
I don't know what your CSS classes are to distinguish between rows and cells so I used 'row', 'firstimage' (which might be defined to take up twice as much width) and 'subsequentimage' as example classes.
I recommend you to use the context variables:
def all_records(request):
records = Record.objects.all().order_by('-created')
newest = records[:5]
oldest = records[5:]
return render(request, 'mycar/allrecords.html', {'newst' : newest,
'oldest': oldest })
In your template :
{% for new in newst %}
<div>what you want with news</div>
{% endfor %}
{% for old in oldest %}
<div>what you want with olds</div>
{% endfor %}

Django: How to output error_message in paragraph <p> instead of list <li> format

I have a simple form which uses a SessionWizardView to spread it over a number of pages. Below is an example of one of the questions.
first_name = forms.CharField(max_length=100, label='What is your first name?', error_messages={'required': 'Please enter your first name'})
Which renders out as
<label for="id_0-first_name">What is your first Name?</label>
<ul class="errorlist">
<li>Please enter your first name</li>
</ul>
<input id="id_0-first_name" maxlength="100" name="0-first_name" type="text" />
Can anyone tell me hwo to change the error output so that it is in <p> Paragraph </p> format rather than <li> List item </li> format?
I am using Django 1.6.2
You'll have to create a class that does renders the HTML as you would want it. See the docs here.
The example from the docs:
from django.forms.util import ErrorList
class DivErrorList(ErrorList):
def __unicode__(self):
return self.as_divs()
def as_divs(self):
if not self: return u''
return u'<div class="errorlist">%s</div>' % ''.join([u'<div class="error">%s</div>' % e for e in self])
f = ContactForm(data, auto_id=False, error_class=DivErrorList)
f.as_p()
You can do as #schillingt suggested and create your own error list class.
Or, if you want to handle this in your template, you can use something like:
<form method="post" action="/some-view/">
... other fields, etc. omitted ...
<!-- The label and form field -->
{{ form.first_name.label_tag }}
{{ form.first_name }}
<!-- Output any errors -->
{% for error in form.first_name.errors %}
<p>{{ error }}</p>
{% endfor %}
... other fields, etc. omitted ...
<button type="submit">Submit</button>
</form>
Update
In order to do this in a cleanly repeatable way, make a template named form-field.html:
{{ field.label_tag }}
{{ field }}
<!-- Output any errors -->
{% for error in field.errors %}
<p>{{ error }}</p>
{% endfor %}
Then, update your main template:
<form method="post" action="/some-view/">
... other fields, etc. omitted ...
{% with field=form.first_name %}
{% include "form-field.html" %}
{% endwith %}
... other fields, etc. omitted ...
<button type="submit">Submit</button>
</form>
You can then make updates to the single form-field.html template and update all of your forms, and it makes your main template a bit simpler

Checking against a unicoded list of strings in template

I have the following code in my template (please note the if statement):
{% for base in bases %}
<label class="checkbox">
<input name="base" value={{ base.id }} type="checkbox"
{% if base.id in selected_bases %}checked="checked" {% endif %}/>
<span>{{ base.name }}</span>
</label>
{% endfor %}
The selected_bases variable is a list of unicoded strings: [u'3', u'1', u'5'].
base.id is an integer.
How can I make them the same type so that if statement does what I need it to?
I don't know if this work, but try this:
{% if value|stringformat:"d" in selected_bases %}
You should probably do this in the view instead, but you can pipe the list values through the add filter, which does type coercion - or pipe the ints to slugify, which will do the reverse. More information here.