I am using Python 2.7 and
I have searched for a solution here, here and here
but none of them did solve my problem
I am working on a simple project (my first one) to practice CRUD using flask and a sqlite database with 2 tables:
Categories
Items
The problem is:
When i load the page the form is correctly displayed and when i select any category and submit, the form just gives me an error when calling
validate_on_submit()
says Not a valid choice with a return value of False
I defined the form like this:
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SelectField
from wtforms.validators import DataRequired
class EditItemForm(FlaskForm):
name = StringField(u'name', validators=[DataRequired()])
description = TextAreaField("Description", validators=[
DataRequired()], render_kw={'rows': '7'})
categories = SelectField(
u'Categories', coerce=str, choices=[], validators=[DataRequired()])
def set_choices(self, list_of_tuples=[]):
self.categories.choices = list_of_tuples
and this is the function to edit an item:
#app.route('/category/<string:category_name>/<string:item_name>/edit/',
methods=['GET', 'POST'])
def editItem(category_name, item_name):
# get the category of the Item
c = getCategory(category_name)
# get the Item
target_item = getItem(c, item_name)
form = EditItemForm()
# FIXME: Complete The process
if request.method == 'POST':
if form.validate_on_submit():
# get selected category
cat = dict(form.categories.choices).get(form.categories.data)
targetCategory = getCategory(cat)
# get identicals to avoid dublication
identicals = getItemIdenticalsInCategory(
targetCategory, form.name.data)
if len(identicals >= 1):
# FIXME: flash the Error in the same page
return "An Item with the same name " + form.name.data + " already exists in " + targetCategory.name
else:
# Edit The Item Data
target_item.name = form.name.data
target_item.description = form.description.data
target_item.category_id = targetCategory.id
# TODO: Flash the response
return redirect(url_for('readCategoryItems',
category_name=category_name))
else:
# display errors
output = ""
for fieldName, errorMessages in form.errors.items():
for err in errorMessages:
# do something with your errorMessages for fieldName
output += fieldName + ":\t" + err + "<hr>"
print(output)
return output
if request.method == 'GET':
# Populate The form with current data
form.name.data = target_item.name
form.description.data = target_item.description
cats = getAllCategories()
list_of_tupels = []
for cat in cats:
session.refresh(cat)
list_of_tupels.append((cat.name, cat.name))
form.set_choices(list_of_tuples=list_of_tupels)
return render_template('updateItem.html',
category_name=category_name,
item_name=item_name, form=form)
and this is the error output:
categories: Not a valid choice<hr>
edit:
*this is what I got when I tried to debug it:
Debug Variables Screenshot
Related
I have a django form with a field directed to a foreignkey. Initially is empty, I mean without any option. Javascript will add options based on another form field.
It's working but I want to test it, without using Selenium (and so, without using javascript).
After numerous attempts I write this code and apparently it's working but I don't know why and I'm not sure it's really working.
def test_form_validation(self):
maschio = Gender.objects.create(name_en='Male', name_it='Maschio')
nome = NameType.objects.create(name_en='Name', name_it='Nome')
romani = NameLanguage.objects.create(
name_en='Romans', name_it='Romani')
romani.syntax.add(nome)
form = NameForm({'nametype': nome.id, 'gender': maschio.id,
'name': 'Remo', 'namelanguage': romani.id})
# print('1', form)
form.fields['nametype'].initial = nome.id
print('2', form)
form.save()
self.assertEqual(Name.objects.all().count(), 1)
my_name = Name.objects.first()
self.assertEqual(my_name.name, 'remo')
self.assertEqual(my_name.nametype, nome)
self.assertEqual(my_name.gender, maschio)
self.assertEqual(my_name.namelanguage, romani)
print('end of test')
# print('1', form) is commented out because with that the test will be give error on line form.save() (ValueError: The Name could not be created because the data didn't validate.). Without that line the test pass (!?!).
I will expected an error when I call my form bounded because nametype has no option to choose from. nome.id is not one of the ammissible choices. And in effect, as I said, it will give an error if I ask to print the form.
<tr><th><label for="id_nametype">Tipo:</label></th><td><select name="nametype" style="width:170px" disabled id="id_nametype">
<option value="0">0</option>
</select></td></tr>
So, is this ok? and why it's working? what's happening?
Thank you
Edit
My form.py:
class NameForm(forms.ModelForm):
"""
form to add names to the database
input: nothing
output: the form
"""
class Meta:
model = Name
fields = ['namelanguage', 'nametype', 'gender', 'name']
widgets = {
'gender': forms.RadioSelect(),
}
def clean_name(self):
data = self.cleaned_data['name'].lower()
if data[0] not in "abcdefghijklmnopqrstuvwxyz":
data = "#" + data
return data
def __init__(self, *args, **kwargs):
try:
last_language = kwargs.get('initial')['namelanguage']
last_nametype = kwargs.get('initial')['nametype']
choices = get_nametype_choices(last_language)
gender_variable = get_gender_variable(last_nametype)
except (TypeError, KeyError):
print('except nameform')
choices = [(0, 0)]
gender_variable = True
super(NameForm, self).__init__(*args, **kwargs)
self.fields['name'].widget.attrs.update({'autofocus': ''})
self.fields['nametype'].widget.attrs.update({'style': 'width:170px'})
self.fields['nametype'].choices = choices
if choices[0][0] == 0:
self.fields['nametype'].disabled = True
if not gender_variable:
print('gender non variable')
self.fields['gender'].disabled = True
self.fields['gender'].required = False
try:
self.fields['gender'].initial = Gender.objects.get(
name_it='neutro').id
except ObjectDoesNotExist:
self.fields['gender'].initial = None
else:
self.fields['gender'].disabled = False
self.fields['gender'].required = True
def get_nametype_choices(last_language):
"""
function called by NameForm__init__ to create the list of choices
for nametype according to the language
input: language
output: a list of tuple with the appropriate nametype for the language
"""
# print('get_nametype_choices')
lista_file = []
try:
language = NameLanguage.objects.get(id=last_language)
choices = language.syntax.all()
for choice in choices:
lista_file.append((choice.id, choice))
except ObjectDoesNotExist:
print('except get_nametype_choices')
lista_file.append((0, 'Scegli il linguaggio'))
return lista_file
get_nametype_choices is not called by the test because last_language = kwargs.get('initial')['namelanguage'] in NameForm.__init__ gives an exception.
I have a requirement to redirect the user to a detail page if there is an exact match on the name property of the shop model. In case of any other property, it should just take to search results page irrespective of a direct match or not.
I am currently using a FacetedSearchCustomView that overrides buid_page, create_reponse and extra_content methods.But I cant find a way to look inside the results of the query as query._results is protected.
Any suggestions on how to go about doing this.
Thanks
UPDATED
FacetedSearchCustomView.py
class FacetedSearchCustomView(FacetedSearchView):
"""
Overrides various default methods to allow for additional context, smoother UX for faceting
"""
def build_page(self):
"""
Paginates the results appropriately.
Overriden to redirect to page 1 if a page_no is not found
"""
try:
page_no = int(self.request.GET.get('page', 1))
except (TypeError, ValueError):
raise Http404("Not a valid number for page.")
if page_no < 1:
raise Http404("Pages should be 1 or greater.")
paginator = NamedPaginator(self.results, on="brand", per_page=self.results_per_page)
# import pdb; pdb.set_trace()
try:
page = paginator.page(page_no)
except InvalidPage:
# Redirect to page 1 of the
path = self.request.path
qs = self.request.GET.copy()
qs['page'] = 1
url = '%s?%s' % (path, qs.urlencode())
return redirect(url)
return paginator, page
def clean_filters(self):
"""Returns a list of tuples (filter, value) of applied facets"""
filters = []
# get distinct facets
facets = list(set(self.form.selected_facets))
for facet in facets:
if ":" not in facet:
continue
field, value = facet.split(":", 1)
field = field.replace('_', ' ').replace('exact', '').title()
filters.append((field, value))
return filters
def create_response(self):
"""
Generates the actual HttpResponse to send back to the user.
Overriding to allow the redirect to pass through from overriden build_page
"""
try:
(paginator, page) = self.build_page()
except ValueError:
return self.build_page()
context = {
'query': self.query,
'form': self.form,
'page': page,
'paginator': paginator,
'suggestion': None,
}
if self.results and hasattr(self.results, 'query') and self.results.query.backend.include_spelling:
context['suggestion'] = self.form.get_suggestion()
context.update(self.extra_context())
return render_to_response(self.template, context, context_instance=self.context_class(self.request))
def extra_context(self):
extra = super(FacetedSearchCustomView, self).extra_context()
extra['filters'] = self.clean_filters()
if not self.results:
extra['facets'] = self.form.search().facet_counts()
else:
extra['facets'] = self.results.facet_counts()
model_type = self.request.path.split('/')[1].rstrip('s')
extra['model_type'] = None if model_type == "search" else model_type
# if model_type in ['package', 'project']:
# extra['facets'] = self.clean_facets(extra['facets'])
# extra['model_create'] = '%s_create' % model_type
return extra
CustomSearchForm.py
class CustomSearchForm(FacetedSearchForm):
def search(self):
sqs = super(CustomSearchForm, self).search()
# We need to process each facet to ensure that the field name and the
# value are quoted correctly and separately:
for facet in self.selected_facets:
if ":" not in facet:
continue
field, value = facet.split(":", 1)
if value:
sqs = sqs.narrow(u'%s:"%s"' % (field, sqs.query.clean(value)))
return sqs
I have implemented something like this in the past by looking at SearchQuerySet.best_match and then comparing the name.
http://django-haystack.readthedocs.org/en/latest/searchqueryset_api.html#SearchQuerySet.best_match
I have a basic form that captures user-submitted data, and sends it in an email via Django's mail module.
But I'd like for the email's subject and message to contain the human-readable version of the choice for order_type.
How do I modify my view to show this?
Here's my form:
from django import forms
from django.contrib.localflavor.us.forms import USPhoneNumberField
import datetime
class OrderForm(forms.Form):
CAKE = 'CA'
CHEESECAKE = 'CH'
COFFEECATERING = 'CO'
BREAKFASTPLATTER = 'BR'
COOKIEPLATTER = 'CP'
GOURMETDESSERTSPLATTER = 'GD'
ORDER_TYPE_CHOICES = (
(CAKE, 'Cake'),
(CHEESECAKE, 'Cheesecake'),
(COFFEECATERING, 'Coffee catering'),
(BREAKFASTPLATTER, 'Breakfast platter'),
(COOKIEPLATTER, 'Cookie platter'),
(GOURMETDESSERTSPLATTER, 'Gourmet desserts platter'),
)
name = forms.CharField(max_length=100, required=True, help_text='100 characters max.')
phone_number = USPhoneNumberField(required=True, help_text='This will allow us to contact you in case of questions regarding your order. Please include your area code.')
email_address = forms.EmailField(required=True, help_text='We will e-mail you to confirm we have received your order, and submitted it to our bakers and baristas.')
date_needed = forms.SplitDateTimeField(required=True, help_text='We can only accept online orders that are placed three days in advance.')
order_description = forms.CharField(required=True, help_text='Please describe your order.')
order_type = forms.ChoiceField(required=True, choices=ORDER_TYPE_CHOICES)
And here's my view:
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from order_form.forms import OrderForm
def order(request):
if request.method == 'POST':
form = OrderForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
order_description = form.cleaned_data['order_description']
phone_number = form.cleaned_data['phone_number']
email_address = form.cleaned_data['email_address']
date_needed = form.cleaned_data['date_needed']
order_type = form.cleaned_data['order_type']
subject = "Order placed for %s by %s" % (order_type, name)
message = "Name: %s\nPhone number: %s\nEmail address: %s\nOrder description: %s\nDate needed: %s" % (name, phone_number, email_address, order_description, date_needed)
from django.core.mail import send_mail
send_mail(subject, message, 'orders#sitename.com', ['anotherperson#sitename.com', email_address])
return HttpResponseRedirect('thanks/')
else:
form = OrderForm()
return render(request, 'order_form/order.html', {
'form': form,
})
I usually just write a simple method for this:
def human_order_type(order_id):
for a_tuple in OrderForm.ORDER_TYPE_CHOICES:
if order_id in a_tuple:
return a_tuple[1]
return None
i'm working on Forms in Django, i have a choice field which contain a list of continents and i need to work on the selected option
this is the Select request from my DB:
def Select_continent():
db = MySQLdb.connect("localhost", "root", "aqw", "PFE_Project")
cursor = db.cursor()
sql = "SELECT Continent FROM myform_servercomponents"
try:
cursor.execute(sql)
results = cursor.fetchall()
continents = []
i=0
for row in results:
continents[i]=row[0]
i+=1
except:
print "Error: unable to fetch data"
db.close()
return continents
and this is my form
def continents():
data = Select_continent()
i=0
continents=[]
for key,value in data:
continents.append(('i',value))
i +=1
return continents
class FilterForm(forms.Form):
Continent = forms.ChoiceField(choices=continents())
so the question is how can i use the selected option in my view
def affiche_all(request, currency):
if request.method == 'POST':
form = FilterForm(request.POST)
if form.is_valid() :
Continent = form.cleaned_data['Continent']
# here i need to send the selected choice to another view
# like this :
# continent == 'Europe'
#url = reverse('affiche_all', args=(), kwargs={'continent': continent})
#return HttpResponseRedirect(url)
any idea ???
I have models.py
class Visit(Model):
reference_visit = models.ForeignKey('self',
help_text="Visit needs a refrence to Prior Visits",
null=True, blank=True)
show_prior_responses = models.BooleanField(default=False,
help_text="Show PriorResponses")
# has many field but i am making it short.
def __unicode__(self):
result = """Visit id:%s pt:%s""" % (self.id, self.patient.id)
return result
forms.py
class VisitSetupForm(Form):
list_visit_ids = ModelChoiceField(
queryset=Visit.objects.none(),
empty_label='Select Revisit ID',required=False)
show_prior_visit = ModelChoiceField(
queryset=User.objects.all(),
empty_label="Select User for Revisit",required = False)
has many but question is on list_visit_ids.
views.py
def setup(request):
"""
Allow an Admin user the ability to setup a patient & visit all at once.
"""
if request.user.is_superuser:
form_class = AdminVisitSetupForm
all_topics = True
else:
form_class = VisitSetupForm
all_topics = False
f = form_class()
# Get a list of topics for each report.
report_topics = {}
for r in Interview.objects.all():
report_topics[r.id] = [t['ad'] for t in r.topics.values('ad')]
data = {
'superuser':request.user.is_superuser,
'report_topics':simplejson.dumps(report_topics)
}
try:
request.user.reviewer
data['reviewer'] = True
except:
pass
if request.method == "POST":
f = form_class(request.POST)
if f.is_valid():
# Create the patient, generate a password, and send them on their way.
cd = f.cleaned_data
patient = None
if cd['revisit']:
# Check for an existing user first.
try:
patient = Patient.objects.get(username=cd['username'])
except Patient.DoesNotExist, e:
data['form'] = f
data['msg'] = 'There is no user with this username.'
return render_to_response('visit/setup.html', data, context_instance=RequestContext(request))
admin_user = get_user(request)
organization = None
if admin_user:
organization = admin_user.organization
if patient and not request.user.is_superuser:
# Make sure the patient they've selected is one of their own.
if patient.organization != organization:
return HttpResponseForbidden('You are not allowed to see this page.')
if not patient:
password = generate_password()
user = User.objects.create_user(cd['username'], cd['contact_email'], password)
user.first_name = cd['first_name']
user.last_name = cd['last_name']
user.save()
patient = Patient(
user=user,
username=user.username,
contact_phone=cd['contact_phone'],
date_of_birth=cd['date_of_birth'],
email=user.email,
first_name=user.first_name,
gender=cd['gender'],
last_name=user.last_name,
maiden_name=cd['maiden_name'],
organization=organization,
patient_type=cd['patient_type'],
security_answer=cd['security_answer'],
security_question=cd['security_question'],
)
patient.save()
# Send them an email.
t = loader.get_template('www/new_account.txt')
c = Context({
'password':'%s-%s-%s' % (password[:3], password[3:5], password[5:]),
'patient':patient
})
msg = t.render(c)
try:
send_mail(
'A request by your physician to do an online medical history before your appointment.',
msg,
'support#careprep.com',
[user.email]
)
except Exception, e:
log.error('Could not send email for new account %s because: [%s]' % (user.username, e))
request.session['password'] = password
# Create the Visit, too.
interview = cd['interview']
list_visit_ids = cd['list_visit_ids']
print list_visit_ids
visit = Visit(
reference_visit = cd['list_visit_ids'],
show_prior_responses = cd['show_prior_responses'],
patient=patient
)
if request.user.is_superuser:
topics = cd['topics']
else:
topics = set(list(interview.topics.all()) + list(cd['topics']))
reviewer_mode = cd.get('reviewer_mode') or patient.patient_type == 'Reviewer'
url, visit = initialize_visit(
request,
patient=patient,
starting_section=interview.starting_section,
visit_title='%s %s' % (patient, interview.title),
topics=topics,
reviewer_mode=reviewer_mode,
chief_complaint=cd['chief_complaint'],
location=cd['interview_site'],
reference_visit = cd['list_visit_ids'],
show_prior_responses = cd['show_prior_responses'],
)
next_url = "/visit/confirmation/%s/%s/?next=%s" % (patient.user.id, interview.id, url)
else:
v = Visit.objects.get(pk=request.POST['list_visit_ids'])
print v
return HttpResponseRedirect(next_url)
# all the fields that are not given pls ignore.
The template is fine.
Now watch forms.py when i do list_visit_ids = ModelChoiceField(queryset=Visit.objects.all(), empty_label='Select Revisit ID',required=False) It works perfectly fine on my local machine.But on my server it has around 6000 visit objects so this page hangs or i should say keep on loading.
So initially i changed it to list_visit_ids = ModelChoiceField(queryset=Visit.objects.none(), empty_label='Select Revisit ID',required=False)
Now i know that by this the form becomes invalid and should go to the else part Now my question how do i make reference_visit=cd['list_visit_ids'] in else (form is invalid)
case save().How do i override the none() attribute.
Thanks in advance i will really appreciate.
If your goal is to save your html page load by removing the 6000 choices (which I've done too: 10000+ <option> fields wrapped by misc html will absolutely choke a page), you shouldn't be using a ChoiceField at all. By setting queryset=Visit.objects.none() you're allowing zero choices and nothing passed in will validate.
You either show 6000 select item drop downs, radio boxes, etc., or find a way to /not/ have a giant select drop down (such as a hidden input or charfield), not fake around a ModelChoiceField who's main purpose is to populate that select drop down and validate.
In short: don't use a ModelChoiceField if you're not going to be using the html choices generated by it. Use something else and do the validation / model pulling yourself via the clean_FOO methods.
class MyForm(forms.Form):
my_input = forms.CharField()
def clean_my_input(self):
input = self.cleaned_data.get('my_input')
try:
return MyModel.objects.get(pk=input) # add a filter here if you want
# (whatever filters you were using in the queryset argument)
except MyModel.DoesNotExist:
raise forms.ValidationError("Doesn't exist / is invalid")
return input