Django MultipleChoiceField restrictions on choices tuple - django

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")

Related

Datefield passing None to post data form a django form

I have a django form that has a datefield in it
class SearchForm(Form):
#otherifields
birth_date = forms.DateField(widget=DateInput(attrs={'classs':'datepicker form-control'}))
In my view i get the forms posted data
post_data = form.cleaned_data
and try to access the date with
date = post_data['birth_date']
but no matter if i set a date on my template or not the value is always None. I use this for the date widget throught all my Django project, and works fine in my ModelForms. But it won't say error of wrong date etc.
my django view
def search(request):
form = SearchForm(request.POST or None)
if request.method == 'POST'::
post_data = form.cleaned_data
customers = Customer.objects.filter(first_name__icontains=post_data['first_name'],
last_name__icontains=post_data['last_name'],
middle_name__icontains=post_data['middle_name'],
gender=post_data['gender'],
email__icontains=post_data['email'],
telephone__icontains=post_data['telephone'],
work_phone__icontains = post_data['work_phone'],
mobile__icontains=post_data['mobile'],
address__icontains = post_data['address'],
region__icontains = post_data['region'],
state__icontains = post_data['state'],
municipality__icontains = post_data['municipality'],
postal_code__icontains = post_data['postal_code'],
country__icontains = post_data['country'],
)
if post_data['birth_date']:
customers = customers.filter(birth_date=post_data['birth_date'])
#some other and returns
But if i print post_data['birth_date'] I always get None. What could be wrong? Am I missing something with forms?
Your code segment
if post_data['birth_date']:
customers.filter(birth_date=post_data['birth_date'])
is outside the scope of the search function. That could also be the reason it's value is None

Add data to ModelForm object before saving

Say I have a form that looks like this:
forms.py
class CreateASomethingForm(ModelForm):
class Meta:
model = Something
fields = ['field2', 'field3', 'field4']
I want the form to have these three fields. However my Somethingclass also has field1. My question is - how do I add data to field1, if I am not using the ModelForm to collect the data. I tried doing something like this, but it isn't working and I am unsure on the proper way to solve this:
views.py
def create_something_view(request):
if (request.method == 'POST'):
# Create an object of the form based on POST data
obj = CreateASomething(request.POST)
# ** Add data into the blank field1 ** (Throwing an error)
obj['field1'] = request.user
# ... validate, save, then redirect
The error I receive is:
TypeError: 'CreateAClassForm' object does not support item assignment
In Django, what is the proper way to assign data to a ModelForm object before saving?
form = CreateASomething(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.field1 = request.user
obj.save()
Sometimes, the field might be required which means you can't make it past form.is_valid(). In that case, you can pass a dict object containing all fields to the form.
if request.method == 'POST':
data = {
'fields1': request.user,
'fields2': additional_data,
}
form = CreateASomethingForm(data)
if form.is_valid():
form.commit(save)
There are two ways given by Django official
LINK : https://docs.djangoproject.com/en/3.0/topics/forms/modelforms/
Method 1]
author = Author(title='Mr')
form = PartialAuthorForm(request.POST, instance=author)
form.save()
Method 2]
form = PartialAuthorForm(request.POST)
author = form.save(commit=False)
author.title = 'Mr'
author.save()
Here is a more suitable way to add data especially used during testing:
First convert an existing entry into a dictionary with the model_to_dict function
from django.forms.models import model_to_dict
...
valid_data = model_to_dict(entry)
Then add the new data into this dictionary
valid_data['finish_time'] = '18:44'
This works better than setting the value in the form
update_form.finish_time = '18:44'
Create the form with the valid data and the instance
update_form = UserEntryForm(valid_data, instance=entry)
Do any assertions you require:
self.assertTrue(update_form.is_valid())
entry = update_form.save()
self.assertEqual(
entry.status,
1
)

Django Multiselect, how to override choices properly

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.

Django - Limit select field to queryset of foreign table?

I have two models as follows:
System_Contact
first_name
last_name
isOwner = CharField ('Y'/'N')
isMainContact = CharField ('Y'/'N')
System
mainContact = ForeignKey(System_Contact)
owner = ForeignKey(System_Contact)
billTo = ForeignKey(System_Contact)
So, when I show the System form in a web page, the user can select the mainContact owner and billTo contacts from a drop down menu to save to the System model. However, I want to filter the select fields in the System form so that they are like this:
mainContact Select box: -- only show System_Contacts that have isMainContact = 'Y'
owner Select Box: -- only show Syste_Contacts that have isOwner = 'Y'
As it is now, I know how to limit a select box by filtering the queryset, but I don't know how to filter the related Foreign Key querySet. Since the mainContact and owner fields are Foreign Keys, I need to filter the Foreign Table (System_Contact), not the table on which the form is built (System)
I know how to filter a normal, non Foreign Key type select box as follows:
form.fields["some_field"].queryset = Some_Model.objects.filter(some_field="Foo")
How would I 'extend' this so that it filters the Foreign table?
This is what I am trying currently, without success:
form.fields["mainContact"].queryset = System_Contact.objects.filter(isMainContact = 'Y')
Thanks
This is what I am trying currently, without success:
form.fields["mainContact"].queryset = System_Contact.objects.filter(isMainContact = 'Y')
Can you include your model form and view? That looks OK to me.
Another approach is to override the __init__ method of your model form and set the queryset there.
class SystemForm(ModelForm):
def __init__(self, *args, **kwargs):
super(SystemForm, self).__init__(*args, **kwargs)
self.fields["mainContact"].queryset = System_Contact.objects.filter(isMainContact = 'Y')
class Meta:
model = System
As an aside, I would recommend using a BooleanField instead of a CharField with 'Y' and 'N' as choices.
That syntax looks correct. Are you receiving an error or is it just not filtering and showing everybody? Try the System_Contact.objects.get(id=<some valid id>) to see if it gets only one or more. If it gets more, perhaps it is being populated from a different call than the one intended.
Well this is embarrassing...
As I was pasting in my view and model form as per Alasdair's request, I noticed my error. Here is my (incorrect) view:
def system_contacts(request, systemID):
sys = System.objects.get(pk=systemID)
if request.method == 'POST':
form = System_Contacts_Form(request.POST, instance=sys)
form.fields["systemOwner"].queryset = System_Contact.objects.filter(systemOwner__exact='Y')
form.fields["mainContact"].queryset = System_Contact.objects.filter(isMainContact__exact = 'Y')
if form.is_valid():
form.save()
return HttpResponseRedirect('/systems/')
else:
conts = Contact_List.objects.filter(systemID = sys.pk)
form = System_Contacts_Form(instance=sys)
return render_to_response('pages/systems/system_pages/contacts.html', {'sys':sys, 'form':form, 'conts':conts}, context_instance=RequestContext(request))
I had put the form.fields["systemOwner"]... part in the POST section of the view, not the GET section of the view.
Here is my corrected view:
def system_contacts(request, systemID):
sys = System.objects.get(pk=systemID)
if request.method == 'POST':
form = System_Contacts_Form(request.POST, instance=sys)
if form.is_valid():
form.save()
return HttpResponseRedirect('/systems/')
else:
conts = Contact_List.objects.filter(systemID = sys.pk)
form = System_Contacts_Form(instance=sys)
form.fields["systemOwner"].queryset = System_Contact.objects.filter(systemOwner__exact='Y')
form.fields["mainContact"].queryset = System_Contact.objects.filter(isMainContact__exact = 'Y')
return render_to_response('pages/systems/system_pages/contacts.html', {'sys':sys, 'form':form, 'conts':conts}, context_instance=RequestContext(request))
Now my corrected view works and the filtering works on the select inputs on the form. I would not have thought to look at that without your help.
Cheers :-)

forms ModelChoiceField queryset + extra choice fields django forms

I am trying to create a form in that ModelChoiceField loads from queryset and i want add few custom values to ModelChoiceField for extend i have used choice field, like below but while updating the form,getting below error
Form Error :
Select a valid choice. That choice is not one of the available choices.
Code :
self.fields['lead'] = forms.ModelChoiceField(queryset = Pepole.objects.filter(poc__in = ('lead','sr.lead')))
self.fields['lead2'] = forms.ModelChoiceField(queryset = Pepole.objects.filter(role__in = ('lead','sr.lead')))
choice_field = self.fields['lead']
choice_field.choices = list(choice_field.choices) + [('None', 'None')]
choice_field = self.fields['lead2']
choice_field.choices = list(choice_field.choices) + [('None', 'None')]
Am i doing any thing wrong here?
That's not going to work. Look at how a ModelChoiceField works:
try:
key = self.to_field_name or 'pk'
value = self.queryset.get(**{key: value})
except self.queryset.model.DoesNotExist:
raise ValidationError(self.error_messages['invalid_choice'])
return value
You can't add something randomly to it.
Use a ChoiceField instead and custom process the data.
class TestForm(forms.Form):
mychoicefield = forms.ChoiceField(choices=QS_CHOICES)
def __init__(self, *args, **kwargs):
super(TestForm, self).__init__(*args, **kwargs)
self.fields['mychoicefield'].choices = \
list(self.fields['mychoicefield'].choices) + [('new stuff', 'new')]
def clean_mychoicefield(self):
data = self.cleaned_data.get('mychoicefield')
if data in QS_CHOICES:
try:
data = MyModel.objects.get(id=data)
except MyModel.DoesNotExist:
raise forms.ValidationError('foo')
return data
It appears that you just want to allow those form fields to be optional. Don't make it hard on yourself. See the documentation regarding marking a form field as required.
lead = forms.ModelChoiceField(queryset=People.objects.filter(poc__in=('lead', 'sr.lead')), required=False)
Do you have a Person object with pk 'None'?
i think you should be using
self.fields['lead'] = forms.ModelChoiceField(queryset = Pepole.objects.filter(poc__in = ('lead','sr.lead')), empty_label="None")
self.fields['lead2'] = forms.ModelChoiceField(queryset = Pepole.objects.filter(role__in = ('lead','sr.lead')), empty_label="None")
edit:
Since you are using a modelchoicefield, i would think that all your choices would be either of that model type or none.
You can "extend" the choices of that type by modifying the queryset you pass into the constructor for the modlechoicefield, e.g.:
qs = People.objects.filter(poc__in = ('lead','sr.lead'))
ext = People.objects.filter(role__in = ('lead', 'sr.lead'))
qs = qs | ext
self.fields['lead'] = forms.ModelChoiceField(queryset = qs, empty_label='None')
or for updating
self.fields['lead'].queryset = qs
this question talks about the modelchoicefield a bit and might be of interest to you:
How do I filter ForeignKey choices in a Django ModelForm?