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 ???
Related
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
I am getting an error message when I try to use MultipleChoiceField with the CheckboxSelectMultiple widget. I built a form using Django's MultipleChoiceField that displays a set of checkboxes for the user to select:
class CheckBoxForm(forms.Form):
def __init__(self,*args,**kwargs):
arg_list = kwargs.pop('arg_list')
section_label = kwargs.pop('section_label')
super(CheckBoxForm,self).__init__(*args,**kwargs)
self.fields['box_ID'].choices=arg_list
print arg_list
self.fields['box_ID'].label=section_label
box_ID = forms.MultipleChoiceField(required=True, widget=forms.CheckboxSelectMultiple)
The view looks like this:
sat_list = (
('a','SAT1'),
('b','SAT2')
)
if request.method == 'POST':
form_scID = CheckBoxForm(request.POST,arg_list=sat_list,section_label="Please Select Satelites")
if form_scID.is_valid():
scID = form_scID.cleaned_data['box_ID']
return HttpResponse("Satellite: {sat}".format(sat=scID,type=taskType))
else:
form_scID = CheckBoxForm(arg_list=sat_list,section_label="Please Select Satelites")
return render(request, 'InterfaceApp/schedule_search.html', {'form3': form_scID})
When I try this I get the error: local variable 'scID' referenced before assignment, but it works when I set up the choices tuple using numbers as the first element, like this:
sat_list = (('1','SAT1'),('2','SAT2'))
Why do I have to set the first element as a number for it to work?
The forms don't have to be unique. You can specify a prefix for the form when you call it:
form_1 = DataTypeForm(request.POST or None,initial_value=True,prefix="1")
form_2 = DataTypeForm(request.POST or None,initial_value=False,prefix="2")
We are using Django 1.4.3 with django-cities-light 2.1.5 to create our website. We are using the form's __init__ method to update the lists of regions and cities. The problem is that if the user selected a city and the form doesn't validate, I want to change the region selection to the region of the selected city. Here is the code:
def __init__(self, instance, *args, **kwargs):
super(ContentItemModelForm, self).__init__(*args, instance=instance, **kwargs)
....
....
....
country_id = None
if ((hasattr(self, "initial")) and ("country_id" in self.initial)):
country_id = int(self.initial['country_id']) if self.initial['country_id'] else None
elif ((hasattr(self, "data")) and (u'country_id' in self.data)):
country_id = int(self.data[u'country_id']) if self.data[u'country_id'] else None
region_id = None
if ((hasattr(self, "initial")) and ("region_id" in self.initial)):
region_id = int(self.initial['region_id']) if self.initial['region_id'] else None
elif ((hasattr(self, "data")) and (u'region_id' in self.data)):
region_id = int(self.data[u'region_id']) if self.data[u'region_id'] else None
city_id = None
if ((hasattr(self, "initial")) and ("city_id" in self.initial)):
city_id = int(self.initial['city_id']) if self.initial['city_id'] else None
elif ((hasattr(self, "data")) and (u'city_id' in self.data)):
city_id = int(self.data[u'city_id']) if self.data[u'city_id'] else None
if (city_id):
city_obj = City.objects.get(geoname_id=city_id)
country_id = city_obj.country_id
region_id = city_obj.region_id
# Set current values of country_id and region_id - commented (not working).
#self.fields['country_id'].initial = country_id
#self.fields['region_id'].initial = region_id
self.fields['region_id'].choices = [("", "(None)", ),]+[(obj.id, obj.name) for obj in Region.objects.filter(country_id=country_id).order_by("name")]
if (region_id):
self.fields['city_id'].choices = [("", "(None)", ),]+[(obj.geoname_id, obj.name) for obj in City.objects.filter(country_id=country_id, region_id=region_id).order_by("name")]
else:
self.fields['city_id'].choices = [("", "(None)", ),]+[(obj.geoname_id, obj.name) for obj in City.objects.filter(country_id=country_id).order_by("name")]
The code that checks the current values of city_id, region_id and country_id is too long and I didn't find a way to shorten it. And the commented lines (self.fields['country_id'].initial = country_id and self.fields['region_id'].initial = region_id) don't work if they are not commented - if only country and city are selected then the selected region is "(None)" (if the form does not validate). If the form validates then we assign the country_id and region_id values according to the selected city (the country and region are not saved to the database).
Here is the city validation:
def clean_city_id(self):
cleaned_data = self.cleaned_data
country_id = cleaned_data['country_id'] if (cleaned_data.has_key('country_id')) else None
region_id = cleaned_data['region_id'] if (cleaned_data.has_key('region_id')) else None
city_id = cleaned_data['city_id'] if (cleaned_data.has_key('city_id')) else None
if (not(city_id)):
if (region_id):
error_message = "If you select a country and a state/region, you must select a city."
self._errors["city_id"] = self.error_class([error_message])
raise forms.ValidationError(error_message)
elif (country_id):
error_message = "If you select a country, you must select a city."
self._errors["city_id"] = self.error_class([error_message])
raise forms.ValidationError(error_message)
return city_id
Do you know how do we assign the correct values to the country and region?
(Edit): We want to have selected="selected" in the HTML in the correct region after a city is selected, and not in the first "(None)" region (also if the user didn't select a region). We use AJAX to update the lists after the user selects a new country or region, but we don't want to use AJAX immediately after loading the form.
Thanks,
Uri.
I would suggest alternative way to solve the problem. Alternative to "manually" setting values for user - let user do it itself. Like this:
1) Trigger form GET on select change event
2) in those event callbacks pass form data to view - just like you do with POST. Just do GET request not POST request.
3) In view just pass request.GET as first parameter to form instead of request.POST
4) Let your existing logic do the magic in form init method.
5) Return form template with correct and valid choices from view
6) switch existing form with new form.
Thats how i would do it anyway. No need to assign anything. Let user do the choices they can after refreshing the form.
I have two models Order and OrderItem.
The process to make an order starts with the OrderItem model.
class OrderItem(SmartModel):
shopping_id = models.CharField(max_length=50,db_index=True)
quantity = models.IntegerField(default=0)
item = models.ForeignKey(Item)
order = models.ForeignKey(Order,null=True,blank=True)
OrderItem represents an item and is a modelform with one field being quantity others are excluded
i validate the form and create the item like so,
def show_item(request,id):
# need to evaluate the HTTP method
if request.method == 'POST':
a = Item.objects.get(pk=id)
form = PartialOrderItemForm(request.POST,instance=a)
# check validation of posted data
if form.is_valid():
order.add_to_order(request,a)
# if test cookie worked, get rid of it
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
url =urlresolvers.reverse('order_index')
# redirect to order page
return HttpResponseRedirect(url)
else:
form = PartialOrderItemForm()
request.session.set_test_cookie()
context={
'form':form,
}
return render_to_response('item.html',context,context_instance=RequestContext(request))
the function called after is_valid i.e order.add_to_order creates and saves an item.
def add_to_order(request,obj):
postdata = request.POST.copy()
#get quantity added, return 0 if empty
quantity = postdata.get('quantity',0)
# fetch the item or return missing page error_message
i = get_object_or_404(Item,pk=obj.id)
# get items in order
order_items = get_order_items(request)
item_in_orders = False
# check to see if item is already in order
for order_item in order_items:
if order_item.item.id == i.id:
#update the quantity if found
order_item.augment_quantity(quantity)
item_in_orders = True
if not item_in_orders:
# creat and save a new order item
anon_user = User.objects.get(id=settings.ANONYMOUS_USER_ID)
oi=OrderItem.objects.create(shopping_id=_shopping_id(request),
quantity=quantity,
item=i,
created_by=anon_user,
modified_by=anon_user)
oi.save()
when a customer is done creating an item(in the database), they fill in a form which is Order
class Order(SmartModel):
#each individual status
SUBMITTED = 1 # the credit card was valid or mobilemoney was recieved.It is ready for us to process the order
PROCESSED = 2 # After submitted orders are reviewed, we can mark them as processed, letting deliverers know order is ready to be shipped
DELIVERED = 3 # the order has been processed and approved by the adminstrator(in this case us), it is delivered.
PICKED_UP =4 # the order has been processed and is picked up by customer
CANCELLED = 5 # Customer called the company and decided they didnt want to go through with the order either by phone or email.
# SET OF POSSIBLE STATUSES
ORDER_STATUSES = ((SUBMITTED,'Submitted'),(PROCESSED,'Processed'),(DELIVERED,'Delivered'),(PICKED_UP,'picked_up'),(CANCELLED,'Cancelled'),)
#Order info
date = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=ORDER_STATUSES, default=SUBMITTED)
# customer = models.ForeignKey(Customer,null=True,blank=True,help_text="The customer who made this order",default=None,)
restaurant = models.ForeignKey(Restaurant,null=True,blank=True,default = None,help_text="The restaurant the customer order from")
#contact info
email = models.EmailField(max_length=50,help_text="Needed as alternative")
mobile = PhoneNumberField(max_length=20,default='+25078######',help_text="Needed to communicate and confirm payment from mobile money")
#billing information
billing_name= models.CharField(max_length=50,help_text="Needed so we can deliver to the right individual")
billing_address= models.CharField(max_length=50,help_text="Needed for delivery purposes, should be office address.")
billing_city = models.CharField(max_length=50,help_text="F4L services are only in selected cities.")
Order is a modelform that i validate and save like so.
def show_checkout(request):
if order.is_empty(request):
cart_url = urlresolvers.reverse('order_index')
return HttpResponseRedirect(cart_url)
if request.method == 'POST':
postdata = request.POST.copy()
form = forms.CheckoutForm(request.POST,postdata)
if form.is_valid():
anon_user = User.objects.get(id=settings.ANONYMOUS_USER_ID)
obj = form.save(commit=False)
obj.created_by = anon_user
obj.modified_by = anon_user
obj.save()
if postdata['submit'] == 'place order':
reciept_url = urlresolvers.reverse('checkout_reciept')
return HttpResponseRedirect(reciept_url)
else:
form = forms.CheckoutForm
context = {
'form':form,
}
return render_to_response('checkout/checkout.html',context,context_instance=RequestContext(request))
i should point out that OrderItem is called before Order..much of where the confusion is coming from
to return all OrderItem objects related to Order i do as suggested by the django documentation https://docs.djangoproject.com/en/dev/topics/db/queries/#following-relationships-backward
>>> from orders.models import OrderItem,Order
>>> a = Order.objects.get(id=1)
>>> a
<Order: blah blah blahbaah#blahblah.com +250780000000 1 2013-02-26 17:25:23.138738+00:00>
>>> a.orderitem_set.all()
[]
I am thinking its empty because i didnt save the foreignkey, but i am really confused about how to go about that. Any help is appreciated..
You just need to pass in (or somehow obtain) the object for the order.
Assuming the PK of the order is in your POST vars, you could do this:
def add_to_order(request,obj):
postdata = request.POST.copy()
order = Order.objects.get(pk=postdata.get('order_id'))
…
oi=OrderItem.objects.create(order=order, shopping_id=_shopping_id(request), …)
Based on your comment below though, you're creating your order AFTER you create the orderitem, making the code above pointless.
If the order hasn't been created yet, there's absolutely no way for you to tie the order item to the order. At least not without doing something else.
Here's what I'd do.
Add a new field to your OrderItem:
order_uuid = models.CharField(max_length=25, blank=False, null=True)
When you start your order process (wherever you start it), create a UUID using python's uuid package:
import uuid
temporary_order_uuid = uuid.uuid4()
Pass this value through each of your order steps, final saving it to the orderitem when you create it.
oi=OrderItem.objects.create(order_uuid=temporary_order_uuid, …)
After you create your order, go back and update all orderitems that contain that uuid with the order's pk:
order_items = OrderItems.objects.get(order_uuid=temporary_order_uuid)
for oi in order_items:
oi.order = order
oi.save()
A much cleaner solution would be to create your orderitems AFTER you create the order in the database, but I don't know all the various requirements of your app.
this is my forms.py
CHOICES = []
class salDeptChartForm(forms.Form):
company = forms.CharField(max_length=2,label = 'Firma',help_text='A valid email address, please.')
date_validfrom = forms.DateField(label = 'Bu Tarihten',required=False)
date_validuntil = forms.DateField(label = 'Bu Tarihe Kadar',required=False)
saldept = forms.MultipleChoiceField(label = 'Satış Departmanları',choices=CHOICES, widget=forms.CheckboxSelectMultiple())
this is where I override the choices in my view.
form = salDeptChartForm(initial={'company':'01'})
saldeptlist = saleinstance.fetchSalDept()
form.fields['saldept'].choices = saldeptlist <this is where I override>
problem occurs when I select one of the options. form doesnt get validate.
Select a valid choice. * is not one of the available choices.
I think, even I override the choices in my view django still checks with previous choices itially I created. I get the correct html output tough.
How to overcome this?
thx
complete view code is there.
form initiates twice one for get and one for post, I dont know if its best either.
def salDept(request):
member_id = request.session['member_id']
saleinstance = sale(member_id)
chartinstance = charts(member_id)
if request.method == 'GET':
form = salDeptChartForm(initial={'company':'01'}) <first init>
saldeptlist = saleinstance.fetchSalDept() <its a list>
form.fields['saldept'].choices = saldeptlist <override choices>
print 'get worked'
return render(request, 'chart/sale/salDept.html',locals())
if request.method == 'POST':
form = salDeptChartForm(request.POST) <second init>
print 'post worked'
if form.is_valid(): <fails>
print 'valid'
company = form.cleaned_data['company']
vfr = form.cleaned_data['date_validfrom']
vun = form.cleaned_data['date_validuntil']
validfrom = formatDate(vfr)
validuntil = formatDate(vun)
selectedSalDepts = request.POST.getlist('saldept')
else:
print 'not valid'
print form.errors
resultdict = chartinstance.salesBySaldept(company,selectedSalDepts,validfrom, validuntil)
form = salDeptChartForm(initial={'company':company,'date_validfrom':request.POST['date_validfrom'], 'date_validuntil':request.POST['date_validuntil']})
domcache = 'true'
return render(request, 'chart/sale/salDept.html',locals())
Okay, you need override the init() of the form to do accomplish this.
class SomeForm(forms.Form):
email = forms.EmailField(label=(u'Email Address'))
users = forms.MultipleChoiceField(choices=[(x, x) for x in User.objects.all()]
)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(SomeForm, self).__init__(*args, **kwargs)
self.fields['users'].choices = [(x, x) for x in User.objects.filter(name__contains='Patel')]
def clean(self):
return self.cleaned_datas
Here in line number (3) you can see that I have provided all the possible choices and then in the init I have filtered the choices, this is important because Django validates your submitted request from the former and displays the choices from the latter
Your validation fails because you only overwrite the choices on the GET method. You don't do anything for the POST, so as far as Django knows, no choice is valid for the POST. Adding the choices to POST should fix your problem.