So... I'm trying to dynamically fill option from a selectfield using data coming from a firestore database.
I initialise a new object from my Items class, I load my form named AjouterForm(), then I call my function getItemsDB() that populate the var fields (it's a list) using data from firestore db, and finally I try to populate the selectfield itemFields.
I can launch the program but when I go on "ajouter" I get an error :
ValueError: too many values to unpack (expected 2)
routes.py
#app.route("/ajouter", methods=['GET', 'POST'])
def ajouter():
try:
items = Items()
form = AjouterForm()
items.getItemsDB()
form.itemsFields.choices = items.fields
except ValueError:
flash("Problème", 'danger')
return render_template('ajouter.html', title='Ajouter', form=form)
ajouter.html
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Ajouter</legend>
<div class="row">
<div class="col">
<div class="form-group">
{{ form.type.label }}
{{ form.type }}
</div>
</div>
<div class="col">
<div class="form-group">
{{ form.customer.label }}
{{ form.customer }}
</div>
</div>
</div>
<div class="output">
<div id="INVOICE" class="hidden INVOICE">
<!--A compléter-->
</div>
<div id="ORDER" class="hidden ORDER">
{{ form.block.label }}
{{ form.block }}
</div>
<div id="SERVICE_REQUEST" class="hidden SERVICE_REQUEST">
<!--A compléter-->
</div>
<div class="output2">
<div id="ITEMS" class="hidden2 ITEMS">
{{ form.itemsFields.label }}
{{ form.itemsFields }}
</div>
<div id="PARTNERS">
</div>
<div id="fields">
</div>
</div>
</div>
</fieldset>
</form>
</div>
forms.py
class AjouterForm(FlaskForm):
type = SelectField("Type", choices=[("INVOICE","invoice"), ("ORDER","order"), ("SERVICE_REQUEST", "service_request")], id="type")
block = SelectField("Block", choices=[("ITEMS","items"), ("PARTNERS","partners"), ("fields","fields")], id="block")
itemsFields = SelectField("Champs", choices=[], coerce=list, id="itemsField")
tailleDocX = IntegerField("Taille X", validators=[DataRequired()])
tailleDocY = IntegerField("Taille Y", validators=[DataRequired()])
customer = SelectField("Customer", choices=[("mlt", "mlt")])
submit = SubmitField('Confirmer')
models.py
class Items(object):
def __init__(self):
self.fields = []
def getItemsDB(self):
doc_ref = db.collection("customizing").document("FORMS")
doc = doc_ref.get()
datas = doc.get("ORDER").get("ITEMS").get("fields")
for data in datas:
self.fields.append(data)
I'm kinda new to python, flask and firestore database and I can't manage to find what is causing this error, hope my english isn't to bad ! :-)
FINALLY I FIXED IT !
I found out that selectfields only accept tuple, I did this :
items = Items()
items.getItemsDB()
form.itemsFields.choices = []
for fields in items.fields:
form.itemsFields.choices += [(fields, fields)]
Related
I really don't understand how to connect these two fields or should I just create one. On my front, I have a starting date and end date I can not connect to the backend properly using django-filters Please see the below code
My filters.py
class VideoFolderFilter(FilterSet):
name = CharFilter(field_name='name', lookup_expr='icontains', widget=forms.TextInput(attrs={'class': "form-control"}))
subscription_plan = ModelChoiceFilter( label='subscription_plan', queryset=Plans.objects.all())
start_date_range = DateFromToRangeFilter(field_name='start_date_range', widget=forms.TextInput(attrs={'class': "form-control"}))
end_date_range = DateFromToRangeFilter(field_name='end_date_range', widget=forms.TextInput(attrs={'class': "form-control"}))
def get_date_range(self, start_date_range, end_date_range):
return Sample.objects.filter(sampledate__gte=start_date_range,
sampledate__lte=end_date_range)
class Meta:
model = VideoFolder
fields = '__all__'
widgets = {'start_date_range': DateInput(),}
exclude = [ 'thumbnail_link', 'link', 'name']
My views.py
#login_required
def search_videos(request):
if Subscriber.objects.filter(user=request.user).exists():
subscription = Subscriber.objects.get(user=request.user)
else:
message = "Seams like you don't have subscription with us please select one of the plans"
return redirect(reverse('main'))
video_filter = VideoFolderFilter(request.GET, queryset=VideoFolder.objects.filter(type='video').order_by('-created_date'))
videos = video_filter.qs
return render(request, 'search-videos.html', locals())
Should I change the views.py to query if yes how to date range and other fields will be included
And my search-videos.html
<div class="search-video-form-section mb-3">
<p>Use one or more filters to search below</p>
<form method="GET">
<div class="form-group mb-px-20 filter-form">
<label class="text-base">Name Contains</label>
<!-- <input type="text" class="form-control" placeholder="Keyword" /> -->
{{ video_filter.form.name }}
</div>
<div class="d-flex flex-wrap date-box">
<div class="form-group input-field">
<label class="text-base">Start Date</label>
<div class="input-group input-daterange">
{{ video_filter.form.start_date_range }}
</div>
</div>
<div class="form-group input-field">
<label class="text-base">End Date</label>
<div class="input-group input-daterange">
{{ video_filter.form.end_date_range }}
</div>
</div>
</div>
<div class="form-group filter-form">
<label class="text-base">Subscription</label>
{{ video_filter.form.subscription_plan }}
</div>
<button class="btn text-white bg-info mt-4">Search</button>
</form>
</div>
I have implemented a Dependent dropdown list within Django but when I try to submit the form I get the following error 'Select a valid choice. That choice is not one of the available choices.'
I have spent a while looking on the web for the answer and have tried a few with little avail.
From my understanding and reading, this is an error because I render the form with a queryset of none. Then I use ajax to fill in the options. Even though I have updated the dropdown list, the form validation is checking my submitted answer against a queryset of none - thus the error.
So i'm hoping someone can help me to update the choices the form will accepted on form submission.
views.py
# stage6 is where I render my view and check validation
def stage6(request):
form_deal = DealForm(request.POST or None, prefix='inst')
if form_deal.is_valid():
form_deal.save()
messages.success(request, 'Deal added successfully.')
form_deal = DealForm()
context = {
'dform': form_deal,
}
return render(request, 'stages/stage6/stage6.html', context)
# This is used for my ajax request
def load_offers(request):
property_process_id = request.GET.get('propertyprocess_link')
offers = Offer.objects.filter(propertyprocess_link=property_process_id).order_by('id')
return render(request, 'stages/stage6/offers_dropdown_list.html', {'offers': offers})
forms.py
class DealForm(forms.ModelForm):
deal_date = forms.CharField(
label='',
widget=forms.TextInput(attrs={'type': 'date'})
)
target_move_date = forms.CharField(
label='',
widget=forms.TextInput(attrs={'type': 'date'})
)
def __init__(self, *args, **kwargs):
super(DealForm, self).__init__(*args, **kwargs)
# filter the foreign keys shown
self.fields['propertyprocess_link'].queryset = PropertyProcess.objects.filter(sector="Sales")
# filter used for ajax request
self.fields['offer_accepted'].queryset = Offer.objects.none()
# add a "form-control" class to each form input
# for enabling bootstrap
for name in self.fields.keys():
self.fields[name].widget.attrs.update({
'class': 'form-control',
})
class Meta:
model = Deal
fields = ('propertyprocess_link',
'deal_date',
'price_agreed',
'target_move_date',
'offer_accepted'
)
models.py
class Deal(models.Model):
propertyprocess_link = models.ForeignKey(PropertyProcess,
on_delete=models.CASCADE)
deal_date = models.DateField()
price_agreed = models.IntegerField()
target_move_date = models.DateField()
offer_accepted = models.ForeignKey(Offer,
on_delete=models.CASCADE)
class Meta:
verbose_name_plural = "deals"
def __str__(self):
return '%s, %s' % (
self.propertyprocess_link.property_link.address_line_1,
self.propertyprocess_link.property_link.postcode
)
html
{% block content %}
<div class="container-fluid header-container">
<div class="row">
<div class="col-sm-9 col-md-7 col-lg-5 mx-auto">
<div class="card-au card-signin my-5">
<div class="card-body">
<form id="offers-form" data-offers-url="{% url 'ajax_load_offers' %}" class=" text-center text-white" method="post" novalidate>
{% csrf_token %}
{{ dform.non_field_errors }}
<div class="form-colour mt-2">
{{ dform.propertyprocess_link.errors }}
<label class="mb-0 mt-1">Property Being Offered On:</label>
{{ dform.propertyprocess_link }}
</div><div class="form-colour mt-2">
{{ dform.offer_accepted.errors }}
<label class="mb-0 mt-1">Offer Being Accepted:</label>
{{ dform.offer_accepted }}
</div>
<div class="form-colour mt-2">
{{ dform.price_agreed.errors }}
<label class="mb-0 mt-1">Price Agreed:</label>
{{ dform.price_agreed }}
</div>
<div class="form-colour mt-2">
{{ dform.deal_date.errors }}
<label class="mb-0 mt-1">Deal Date:</label>
{{ dform.deal_date }}
</div>
<div class="form-colour mt-2">
{{ dform.target_move_date.errors }}
<label class="mb-0 mt-1">Target Move Date:</label>
{{ dform.target_move_date }}
</div>
<div class="mb-3"></div>
{# hidden submit button to enable [enter] key #}
<div class="hidden-btn" style="margin-left: -9999px"><input class="hidden-btn" type="submit" value=""/></div>
<div class="text-center mt-2">
<input type="submit" class="login-btn btn-green btn btn-lg border-green text-uppercase py-3" value="Add Deal" />
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
{% block postloadjs %}
{{ block.super }}
<script>
$("#id_inst-propertyprocess_link").change(function () {
var url = $("#offers-form").attr("data-offers-url"); // get the url of the `load_offers` view
var propertyID = $(this).val(); // get the selected Property Process ID from the HTML input
$.ajax({ // initialize an AJAX request
url: url, // set the url of the request (= localhost:8000/ajax/load-offers/)
data: {
'propertyprocess_link': propertyID // add the Property Process id to the GET parameters
},
success: function (data) { // `data` is the return of the `load-offers` view function
$("#id_inst-offer_accepted").html(data); // replace the contents of the offers input with the data that came from the server
}
});
});
</script>
{% endblock postloadjs %}
Thanks very much for any help anyone can give.
I'm using two forms in one of my pages one for closing tickets and one for sending replies.
But problem is when I submit one of them another one is submitted too! and shows the flash massages. So I have two flash massages !
That gets more complicated since I'm checking some conditions to show first form and in that case which that form even doesn't again I have two flash massages !
#app.route('/edit-ticket', methods=['GET', 'POST'])
def edit_ticket():
if session['logged_in'] == True:
trackingNumberLink = int(request.args.get('trackingNumber'))
closeForm = CloseTicket()
editForm = EditTicket()
GetTicketStatus = tickets.find_one({"trackingNumber": trackingNumberLink})
if closeForm.validate_on_submit():
tickets.update_one({"trackingNumber": trackingNumberLink},
{"$set": {"status": "پاسخ داده شده", "order": 2}})
flash(u"تیکت مورد نظر با موفقیت بسته شد.")
if editForm.validate_on_submit():
replyDate = jdatetime.datetime.now()
tickets.update_one({"trackingNumber": trackingNumberLink},
{"$set": {"status": "در حال بررسی", "order": 1}})
tickets.update_one({"trackingNumber": trackingNumberLink},
{"$push": {"replies": {"rep": {"mass": editForm.ticketMassage.data,
"date": replyDate.strftime("%H:%M:%S %d-%m-%y "),
"HowSent": "user"}}}})
flash(u"پاسخ با موفقیت ارسال شد.")
return render_template('edit-ticket.html', Title="ویرایش تیکت", closeForm=closeForm,
editForm=editForm, CanUserCloseTicket=GetTicketStatus)
else:
return redirect(url_for('Login'))
HTML:
{% extends "layout.html" %}
{% block content_main %}
<div class="container content-box">
<div class="row">
<div class="col-sm-12">
<div class="FormSection center-form">
<fieldset class="form-group">
<legend class="border-bottom mb-4">ویرایش تیکت</legend>
</fieldset>
<form method="post" action="">
{{ editForm.hidden_tag() }}
<div class="form-group">
{{ editForm.ticketMassage.label(class="form-control-label") }}
{% if editForm.ticketMassage.errors %}
{{ editForm.ticketMassage(class="form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in editForm.ticketMassage.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ editForm.ticketMassage(class="form-control-lg") }}
{% endif %}
</div>
<div class="form-group">
{{ editForm.submit(class="btn btn-outline-info") }}
</div>
</form>
</div>
{% if CanUserCloseTicket['status'] != "پاسخ داده شده" %}
<div class="FormSection center-form">
<form method="post" action="">
{{ closeForm.hidden_tag() }}
<div class="form-group">
{{ closeForm.submitCloseTicket(class="btn btn-outline-info") }}
</div>
</form>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock content_main %}
Forms Class:
class EditTicket(FlaskForm):
ticketMassage = TextAreaField('متن پیام:',
description=u'پاسخ خود را بنویسید.',
validators=[data_required(), Length(min=20, max=500)], )
submit = SubmitField('ویرایش تیکت')
class CloseTicket(FlaskForm):
submitCloseTicket = SubmitField('بستن تیکت')
Render the form tag with an id attribute and for the submit and input tags use the form attribute.
<form id="edit-ticket">
{{ form.submit(form="edit-ticket") }}
The form element that the input element is associated with (its form owner). The value of the attribute must be an id of a element in the same document. If this attribute isn't used, the element is associated with its nearest ancestor element, if any. This attribute lets you to place elements anywhere within a document, not just as descendants of form elements.
Update Use different names for submit then in your views.py if close_form.validate_on_submit() and close_form.close.data:
from flask import Flask, render_template
from flask_wtf import FlaskForm, CSRFProtect
from wtforms.fields import SubmitField, TextAreaField
app = Flask(__name__)
app.config['SECRET_KEY'] = '^%huYtFd90;90jjj'
app.config['UPLOADED_FILES'] = 'static/files'
csrf = CSRFProtect(app)
class EditTicketForm(FlaskForm):
ticket_message = TextAreaField()
edit = SubmitField()
class CloseTicketForm(FlaskForm):
message = TextAreaField()
close = SubmitField()
#app.route('/edit-ticket', methods=['GET', 'POST'])
def edit_ticket():
close_form = CloseTicketForm()
edit_form = EditTicketForm()
if close_form.is_submitted() and close_form.close.data:
if close_form.validate():
x = close_form.message.data
return x.upper()
if edit_form.is_submitted() and edit_form.edit.data:
if edit_form.validate():
y = edit_form.ticket_message.data
return y.upper()
return render_template('edit-ticket.html', close_form=close_form, edit_form=edit_form)
if __name__ == "__main__":
app.run(debug=True)
edit-ticket.html
<form method="post" id="edit-form" novalidate></form>
<form method="post" id="close-form" novalidate></form>
{{ edit_form.csrf_token(form="edit-form") }}
{{ close_form.csrf_token(form="close-form") }}
{{ edit_form.ticket_message(form="edit-form") }}
{{ edit_form.edit(form="edit-form") }}
{{ close_form.message(form="close-form") }}
{{ close_form.close(form="close-form") }}
Put an action on your forms instead of leaving it blank:
<form method="post" action="/close-ticket"> ... </form>
<form method="post" action="/edit-ticket"> ... </form>
Define explicit functions to handle each action. One action, one function - keep it simple. Split out and re-use the logged-in logic for each.
#app.route('/close-ticket', methods=['POST'])
def close_ticket():
if session['logged_in'] != True:
return redirect(url_for('Login'))
# closeForm handling, etc....
#app.route('/edit-ticket', methods=['POST'])
def edit_ticket():
if session['logged_in'] != True:
return redirect(url_for('Login'))
# editForm handling, etc....
I have a model that describes a Webpage. The source_upload field represents a screenshot of the webpage.
For adding site-objects to my application, I use a django class-based CreateView. This works really well.
Now I'm trying to add a semi-automatic way of adding sites. You can pass an URL to the view and the view fills the form automatically (and makes a screenshot of the webpage). The user should be able to review all the auto-extracted fields - especially the auto generated screenshot image - change them and hit the save button to add the object to the database and the image (if approved) to its final location.
I tried to implement this in the get_initial method of the view. This works quite well except for the screenshot-FileField. The path I set in initial['source_upload'] is not shown in the current: <link>part of the FileInput widget of the form.
How can I give the filefield an initial value?
models.py
class Site(models.Model):
def get_source_upload_path(instance, filename):
now = datetime.datetime.now()
return "appname/sites/{}/{}/{}/site_{}_{}".format(now.year, now.month, now.day, instance.pk, filename)
creationDate = models.DateTimeField(auto_now_add=True)
last_modifiedDate = models.DateTimeField(auto_now=True)
creator = models.ForeignKey('auth.User', related_name='siteCreated')
last_modifier = models.ForeignKey('auth.User', related_name='siteLast_modified')
date = models.DateTimeField(default=datetime.date.today)
title = models.CharField(max_length=240, blank=True)
body = models.TextField(max_length=3000)
source_url = models.URLField(blank=True)
source_upload = models.FileField(upload_to=get_source_upload_path, blank=True)
keywords = models.ManyToManyField("Keyword")
urls.py
url(r'site/add/$', views.SiteCreate.as_view(), name='site-add'),
url(r'site/add/(?P<source_url>[A-Za-z0-9\-._~:/\[\]#!$&\'\(\)\*\+,;=?#]+)/$', views.SiteCreate.as_view(), name='site-add-fromurl'),
forms.py
class SiteForm(ModelForm):
class Meta:
model = Site
fields = ['date', 'title', 'body', 'source_url', 'source_upload', 'keywords']
widgets = {
'keywords' : CheckboxSelectMultiple(),
}
views.py
class SiteCreate(LoginRequiredMixin, CreateView):
model = Site
template_name = 'appname/site_form.html'
form_class = SiteForm
success_url = reverse_lazy('appname:index')
def form_valid(self, form):
form.instance.creator = self.request.user
form.instance.last_modifier = self.request.user
return super(SiteCreate, self).form_valid(form)
def get_initial(self):
# Get the initial dictionary from the superclass method
initial = super(SiteCreate, self).get_initial()
try:
#get target url from request
fullpath = self.request.get_full_path()
fullpath = fullpath.split("/")
fullpath, querystring = fullpath[3:-1], fullpath[-1]
source_domain = fullpath[2]
fullpath = "/".join(fullpath)
fullpath += querystring
source_url = fullpath
if (not source_url.startswith("http://") and not source_url.startswith("https://")):
print("ERROR: url does not start with http:// or https://")
return initial
# ...
# extract title, date & others with BeautifulSoup
# ...
#extract screenshot (is there a better way?)
from selenium import webdriver
driver = webdriver.Firefox()
driver.get(source_url)
tmpfilename = "{}_{}.png".format(get_valid_filename(source_domain), get_valid_filename(title[:30]))
now = datetime.datetime.now()
tmpfilepath_rel = "appname/sites/tmp/{}/{}/{}/{}".format(now.year, now.month, now.day, tmpfilename)
tmpfilepath = settings.MEDIA_ROOT + tmpfilepath_rel
folder=os.path.dirname(tmpfilepath)
if not os.path.exists(folder):
os.makedirs(folder)
driver.save_screenshot(tmpfilepath)
driver.quit()
initial = initial.copy()
initial['source_url'] = source_url
initial['title'] = title
initial['date'] = soup_date
initial['body'] = body
initial['source_upload'] = tmpfilepath_rel
except KeyError as e:
print("no valid source_url found. zeige also ganz normales add/new template")
except IndexError as e:
print("no valid source_url found. zeige also ganz normales add/new template")
return initial
site_form.html (Used for Create and Update view)
{% extends "appname/base.html" %}
{% load staticfiles %}
{% block header %}
<link rel="stylesheet" type="text/css" href="{% static 'appname/model_forms.css' %}" />
{% endblock %}
{% block body %}
<form enctype="multipart/form-data" action="" method="post">{% csrf_token %}
<div class="fieldWrapper">
<div class="error">{{ form.date.errors }}</div>
<div class="label">{{ form.date.label_tag }}</div>
<div class="field">{{ form.date }}<br />{{ form.date.help_text }}</div>
<div class="floatclear"></div>
</div>
<div class="fieldWrapper">
<div class="error">{{ form.title.errors }}</div>
<div class="label">{{ form.title.label_tag }}</div>
<div class="field">{{ form.title }}<br />{{ form.title.help_text }}</div>
<div class="floatclear"></div>
</div>
<div class="fieldWrapper">
<div class="error">{{ form.body.errors }}</div>
<div class="label">{{ form.body.label_tag }}</div>
<div class="field">{{ form.body }}<br />{{ form.body.help_text }}</div>
<div class="floatclear"></div>
</div>
<div class="fieldWrapper">
<div class="error">{{ form.source_url.errors }}</div>
<div class="label">{{ form.source_url.label_tag }}</div>
<div class="field">{{ form.source_url }}<br />{{ form.source_url.help_text }}</div>
<div class="floatclear"></div>
</div>
<div class="fieldWrapper">
<div class="error">{{ form.source_upload.errors }}</div>
<div class="label">{{ form.source_upload.label_tag }}</div>
<div class="field">{{ form.source_upload }}<br />{{ form.source_upload.help_text }}</div>
<div class="floatclear"></div>
</div>
<div class="fieldWrapper">
<div class="error">{{ form.keywords.errors }}</div>
<div class="label">{{ form.keywords.label_tag }}</div>
<div class="field">
<ul class="checkbox-grid">
{% for kw in form.keywords %}
<li>
{{ kw.tag }}
<label for="{{ kw.id_for_label }}">
{{ kw.choice_label }}
</label>
</li>
{% endfor %}
</ul>
<div class="checkbox_help_text"><br />{{ form.keywords.help_text }}</div>
</div>
<div class="floatclear"></div>
</div>
<input type="submit" value="Save" />
</form>
<div id="ObjectHistory">
{% if site.pk %}
<p>Created by: {{ site.creator }}</p>
<p>Created on: {{ site.creationDate }}</p>
<p>Last modified by: {{ site.last_modifier }}</p>
<p>Last modified on: {{ site.last_modifiedDate }}</p>
<p>Now: {% now "Y-m-d H:i:s" %} <button>delete</button></p>
{% else %}
<p>This is a new Site!</p>
<p>Now: {% now "Y-m-d H:i:s" %}</p>
{% endif %}
</div>
{% endblock %}
This is because the value of FileField, as used by your form, isn't just the path to the file - it's an instance of FieldFile (see https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.fields.files.FieldFile).
I'm not sure if you can instantiate a FieldFile directly, but at least you can do it by instantiating the model (you don't need to save it).
In views.py:
tmp_site = Site(source_upload=tmpfilepath_rel)
initial['source_upload'] = tmp_site.source_upload
Alternatively you can manually add a link to the file when rendering the html:
<div class="currently">{{ form.source_upload.value }}</div>
My form is like this:
forms.py
class FollowUpForm(Form):
assigned_to_user_id = SelectField("Assigned to", coerce=int)
planned_followup_date = DateField("Planned Followup Date", [validators.DataRequired("Date is required")], format="%Y-%b-%d")
actual_followup_date = DateField("Actual Followup Date",format="%Y-%b-%d")
notes = TextAreaField("Notes", [validators.DataRequired("Notes is required")])
status = SelectField("Status", choices = zip(status_categories, status_lebels))
I don't want to validate the actual_followup_date field but want to keep the date format .
My html form is like this
<div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" id="FollowUpFormPopUp">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><div aria-hidden="true">×</div></button>
<h4 class="modal-title" id="myModalLabel"><strong> Followup Form</strong></h4>
</div>
<div class="modal-body">
<div class="form-wrap">
<form id="SubmitFollowUpForm">
<div class="form-group" >
{{ followup_form.assigned_to_user_id.label }}
{{ followup_form.assigned_to_user_id(class_="form-control", type="select") }}
<span class="help-block"></span>
</div>
<div class="form-group " >
{{ followup_form.planned_followup_date.label }}
{{ followup_form.planned_followup_date(class_="form-control", **{"data-parsley-required":"true",
"data-parsley-required-message":"Planned Followup Date is required",
"data-provide":"datepicker",
"data-date-format":"yyyy-M-dd"}) }}
<span class="help-block"></span>
</div>
<div class="form-group " >
{{ followup_form.actual_followup_date.label }}
{{ followup_form.actual_followup_date(class_="form-control", **{"data-parsley-required":"false",
"data-provide":"datepicker",
"data-date-format":"yyyy-M-dd"}) }}
<span class="help-block"></span>
</div>
<div class="form-group " >
{{ followup_form.notes.label }}
{{ followup_form.notes(class_="form-control", rows=10, type="text", **{"data-parsley-required":"true",
"data-parsley-required-message":"Notes is required",
}) }}
<span class="help-block"></span>
</div>
<div class="form-group" >
{{ followup_form.status.label }}
{{ followup_form.status(class_="form-control", type="select") }}
<span class="help-block"></span>
</div>
<button type="submit" class="btn btn-primary btn-block" id="FollowUpSubmitBtn">SUBMIT</button>
</form>
</div>
</div>
</div>
</div>
</div>
When I submit the form without providing any value to the Actual_followup_date..its getting error "not a valid date format" . But in this situation I want to store null value to database and not want this span error. you can see the screen-shot of this error..
Please suggest me..thanks..
You need to add a validator of type Optional to your Actual Followup Date field like so:
from wtforms.validators import ..., Optional
...
actual_followup_date = DateField("Actual Followup Date",format="%Y-%b-%d",[validators.Optional()])
...
this will allow you to not enter anything, and will just push the None value to the database.
Create a custom validator.
def customValidatorForFollowupDate(form, field):
if not form.actual_followup_date.data:
field.errors[:] = []
raise StopValidation()
And use this like this way-
class FollowUpForm(Form):
assigned_to_user_id = SelectField("Assigned to", coerce=int)
planned_followup_date = DateField("Planned Followup Date", [validators.DataRequired("Date is required")], format="%Y-%b-%d")
actual_followup_date = DateField("Actual Followup Date",[customValidatorForFollowupDate], format="%Y-%b-%d")
notes = TextAreaField("Notes", [validators.DataRequired("Notes is required")])
status = SelectField("Status", choices = zip(status_categories, status_lebels))