Django Multiselect, how to override choices properly - django

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.

Related

Passing default values into an unbound django form

I have two select classes that I am trying to create in an unbound form. The data selections are only relevant to the presentation that is created in the view, so are throwaways and do not need to be saved in a model.
The challenge I have is that I can pass in the field listings ok, but how do I set "default" checked / selected values so that the form becomes 'bound'?
views.py
def cards(request):
sort_name = []
sort_name.append("Alphabetic Order")
sort_name.append("Most Popular")
sort_name.append("Least Popular")
sort_name.append("Highest Win Rate")
sort_name.append("Lowest Win Rate")
sort_id = range(len(sort_name))
sort_list = list(zip(sort_id, sort_name))
<more code to make filt_list and zip it>
if request.method == 'POST':
form = cardStatsForm(request.POST, sortList=sort_list, filtList=filt_list)
if form.is_valid():
do something
else:
do something else
else:
form = cardStatsForm(filter_list, sort_list)
forms.py
class cardStatsForm(forms.Form):
def __init__(self, filterList, sortList, *args, **kwargs):
super(cardStatsForm, self).__init__(*args, **kwargs)
self.fields['filts'].choices = filterList
self.fields['filts'].label = "Select player rankings for inclusion in statistics:"
self.fields['sorts'].choices = sortList
self.fields['sorts'].label = "Choose a sort order:"
filts = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=(), required=True)
sorts = forms.ChoiceField(choices=(), required=True)
The difficulty I am having is the the form fails the "is_valid" test since it is not bound, and I have the "required=true" setting (so that the user must select a checkbox / select a value), but I cannot enforce the logic since it seems the form is never 'bound'.
You can use django forms validation or pass defult value in your views.py. It will return unbound forms if value doesn't match with your default value.
let show you how to do it in your views.py:
error_message = None
default_value = "jhone"
if form.is_valid():
name = request.POST['name']
defult_name = jhone
if defult_name != name:
error_message = 'Name must be jhone'
if not error_message:
form.save() #it will only save forms if default value match
else:
do something else
context = {'error_message':error_message,
'default_value': default_value,
'form':form,
} #pass the context in your html template for showing default value and error message
in your .html
{{error_message}}
<input type=text name='name' {%if form.is_bound %} value="{{default_value}} {%endif%}">
I was able to correct my issue by adding "inital=0" and modifying my form call as outlined below:
forms.py
filts = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=(), initial=0, required=True)
sorts = forms.ChoiceField(choices=(), initial=0, required=True)
views.py
if request.method == 'POST':
form = cardStatsForm(data=request.POST, sortList=sort_list, filterList=filter_list)
else:
form = cardStatsForm(filter_list, sort_list)

Testing a form. What'happening? is this really working?

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.

django prepopulate modelform - nothing happens

This must be a very simple thing however I can not seem to get through it..
I trying to build a form where the user can update a ModelForm. First he inserts a user id and afterwards I want to show him the form pre-populate with the original data so he can change only the fields that he wants.
After some the help of my friend google, stackoverflow and the django documentation, I've come to this:
views.py
user = User.objects.get(user_id=usr)
if request.method == 'POST':
form = TableForm(request.POST)
if form.is_valid():
#do something
else:
form = TableForm(instance=user)
return render_to_response('template.html',{'form':form})
forms.py
class TableForm(forms.ModelForm):
pres_clinic = forms.ModelChoiceField(queryset=PresClinic.objects.all(),
widget=SelectWithPop(), label=ugettext("Clinic presentation"),
required=False)
MAYBECHOICES = (
('', '---------'),
(ugettext('Yes'), ugettext('Yes')),
(ugettext('No'), ugettext('No')))
bcg_scar = forms.ChoiceField(choices=MAYBECHOICES, label=ugettext(
"BCG scar"), required=False)
mantoux_register = forms.ChoiceField(choices=MAYBECHOICES,
label=ugettext("Mantoux register"), required=False)
date_diag = forms.DateField(widget=DateTimeWidget, label=ugettext(
"Diagnosis date"), required=False)
situation = forms.ModelChoiceField(queryset=Situation.objects.all(),
widget=SelectWithPop(), label=ugettext("Patient status"),
required=False)
date_situation = forms.DateField(widget=DateTimeWidget, label=ugettext(
"Date patient status"), required=False)
class Meta:
model = Table
fields = ('pres_clinic', 'bcg_scar', 'mantoux_register',
'date_diag', 'situation', 'date_situation')
def clean(self):
cleaned_data = self.cleaned_data
diag = cleaned_data.get('date_diag')
errors = []
now = datetime.date.today()
if diag is not None and diag != u'':
if diag > now:
errors.append(ugettext('The field "Diagnosis date" should be '
'smaller than the actual date'))
if errors:
raise ValidationError(errors)
return cleaned_data
template:
{{ form }} # presents the empty form and not the data from that user
The version of django is 1.4
Can anyone tell me what is wrong and why I'm not able to see the form populated?
Thank you very much
You need to define a dictionary to be used for the initial data and change from TableForm(instance=user) to TableForm(initial=dict), for example something like:
user = User.objects.get(user_id=usr)
if request.method == 'POST':
form = TableForm(request.POST)
if form.is_valid():
#do something
else:
data = {'pres_clinic' : 'value', 'bcg_scar' : 'value', 'mantoux_register' : 'value'}
form = TableForm(initial=data)
return render_to_response('template.html',{'form':form})
I would also put the render to response out of the if statement so if the form isn't valid the page should reload and show any errors.
More information on the django docs here
I hope this helps!
You can try 'model_to_dict'
from django.forms.models import model_to_dict
user = User.objects.get(user_id=usr)
if request.method == 'POST':
form = TableForm(request.POST)
if form.is_valid():
#do something
else:
form = TableForm(initial=model_to_dict(user))
return render_to_response('template.html',{'form':form})

how to overide in forms queryset none() attribute and somehow allow to save the field?

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

Django-forms validation problem at initialization

I have a problem about validation of form fields in Django.
I have a profile_edit form it has a set of clean functions. Also, in order to control the current password, I need to pass current user's password to the form.
email = forms.EmailField(label=_("member_Email"),required = True)
currentPassword = forms.CharField(label=_("member_currentPassword"),widget=forms.PasswordInput,required=False)
newPassword = forms.CharField(label=_("member_newPassword"),widget=forms.PasswordInput,required=False)
newPasswordRe = forms.CharField(label=_("member_newPasswordRe"),widget=forms.PasswordInput,required=False)
emailPreference = forms.ChoiceField(label=_("member_email_preference"), widget=forms.RadioSelect(renderer=HorizRadioRenderer),choices = UserMailPreference.USER_MAIL_PREF,required = True)
gender = forms.ChoiceField(label=_("member_gender"), widget=forms.RadioSelect(renderer=HorizRadioRenderer),choices = UserGender.USER_GENDER ,required = False)
birthYear = forms.ChoiceField(label=_("member_birthyear"),required = False)
education = forms.ChoiceField(label=_("member_education"),choices = UserEducation.USER_EDU, required = False)
def __init__(self,*args, **kwargs):
super(MemberSettings,self).__init__(*args, **kwargs)
now = datetime.datetime.now()
self.fields["birthYear"].choices = birthYearList
def set_user(self, user):
self.user = user
def clean_email(self):
field_data = self.cleaned_data['email']
if not field_data:
return ''
try:
u = User.objects.get(email=field_data)
if not u.id == self.user.id:
raise forms.ValidationError(_('err_already_registered'))
except ObjectDoesNotExist:
pass
return field_data
By using the set_user method I can pass the current user to form however, when I write
form = MemberSettings(default_data)
form.set_user(u)
in my view, form tries to validate all the fields although I just try to initialize the form.
But if I do not set the user, form works normal.
What is the problem that I can't notice ?
Thanks
See my answer on: Django form edit problem at intiliazation
It's the same issue. You need to use the initial argument when passing data to the form.