Django How to work with MultipleChoiceField - django

form.py:
CHECKBOX_CHOICES = (
('Value1','Value1'),
('Value2','Value2'),
)
class EditProfileForm(ModelForm):
interest = forms.MultipleChoiceField(required=False,
widget=CheckboxSelectMultiple(),
choices=CHECKBOX_CHOICES,)
def save(self, *args, **kwargs):
u = self.instance.user
u.interest = self.cleaned_data['interest']
u.save()
profile = super(EditProfileForm, self).save(*args,**kwargs)
return profile
it saves in db as [u'value1', u'value2']
Now, How can I only render in my template to show as string like value1, value2 without [u' '] or is there a better way to save the value as a string?

u.interest = u','.join(self.cleaned_data['interest'])

Related

django form.is_valid returns false after adding fields dynamically to the form

I have a form like this,
class UniqueUrlForm(forms.ModelForm):
cc_number = cc_form.CardNumberField(label='Card Number')
cc_expiry = cc_form.CardExpiryField(label='Expiration Date')
cc_code = cc_form.SecurityCodeField(label='CVV/CVC')
class Meta:
model = Transactions
fields = ['customer_name', 'customer_phone', 'customer_email', 'total_amount', 'cc_number', 'cc_expiry',
'cc_code']
def __init__(self, *args, **kwargs):
super().__init__()
store_id = kwargs.get("store_id", "1")
payment_page = get_object_or_404(
PaymentPageDisplayDetails.objects.filter(store_id=store_id).values("payment_fields_visible"))
with urllib.request.urlopen(payment_page['payment_fields_visible']) as url:
display_fields = json.loads(url.read().decode())
for field_name in display_fields:
self.fields[field_name] = forms.CharField(required=False)
and a view like this,
def getpaymentpage(request, store_identifier):
uniqueurl_form = UniqueUrlForm(request.POST or None, request.FILES or None, {"store_id": 1})
if uniqueurl_form.is_valid():
trx_details = {
"amount": uniqueurl_form.cleaned_data['amount'],
"customer_email": uniqueurl_form.cleaned_data['customer_email'],
"customer_phone": uniqueurl_form.cleaned_data['customer_phone'],
"cc_number": uniqueurl_form.cleaned_data['cc_number'],
"cc_name": uniqueurl_form.cleaned_data['customer_name'],
"cc_month": uniqueurl_form.cleaned_data['cc_month'],
"cc_year": uniqueurl_form.cleaned_data['cc_year'],
"cvv": uniqueurl_form.cleaned_data['cvv'],
}
return HttpResponse(trx_details)
context = {
'form': {
uniqueurl_form,
},
"page": store_display,
}
return render(request, 'unique_url.html', context)
I have tried print(uniqueurl_form.errors) it always returns empty and uniqueurl_form.is_valid() as false.
Is it because I'm adding dynamic fields to the form.
I have referred the following,
dynamically add field to a form
What am I doing wrong here?
Thank you for your suggestions.
weirdly it started working when i made following changes,
class UniqueUrlForm(forms.ModelForm):
cc_number = cc_form.CardNumberField(label='Card Number')
cc_expiry = cc_form.CardExpiryField(label='Expiration Date')
cc_code = cc_form.SecurityCodeField(label='CVV/CVC')
class Meta:
model = Transactions
fields = ['customer_name', 'customer_phone', 'customer_email', 'total_amount', 'cc_number', 'cc_expiry',
'cc_code']
def __init__(self, *args, **kwargs):
store_id = kwargs.get("store_id", "1")
super(UniqueUrlForm, self).__init__(*args, **kwargs)
payment_page = get_object_or_404(
PaymentPageDisplayDetails.objects.filter(store_id=store_id).values("payment_fields_visible"))
with urllib.request.urlopen(payment_page['payment_fields_visible']) as url:
display_fields = json.loads(url.read().decode())
for field_name in display_fields:
self.fields[field_name] = forms.CharField()
my guess is because I did not specify class name in my .super() event though it was appending the fields it was not sure what validations to put on those fields.

Render django.forms.fields.ChoiceField object

In my Django Project I have the following Problem:
I would like to have a dynamic Django form. In the first step the user is asked something by the first form. When I get the postmethod the variables should be used for genereating a new form
my views.py
def calc(request):
if request.method =="POST":
get_form = CalculationForm(request.POST)
if get_form.is_valid():
op = get_form.cleaned_data['op']
ab = get_form.cleaned_data['ab']
alternative = AlternativForm(optype = op, wsgroup = ab)
return render(request, 'calculated_lensar.html', {"alternativ" : alternativ})
else:
form = CalculationForm()
return render(request, 'calc.html', {'form': form})
The secondform (postmethod) looks like
class AlternativForm(forms.Form):
praep_button = ((3, 'hallo'), (4, 'tschüss'))
def __init__(self, optype, wsgroup, *args, **kwargs):
super(AlternativForm, self).__init__(*args, **kwargs) #dont know for what this is standing
self.optype = optype
self.wsgroup = wsgroup
self.values = self.read_db()
self.praep_button = self.buttons()
self.felder = self.blub()
self.neu2 = self.myfield_choices()
def read_db(self):
import sqlite3
....
return result #tuple with 15x5 elements
def buttons(self):
praep_button = []
for i in self.values:
praep_button.append((i[4], i[1]))
return praep_button #Just formating result from read_db in tuple(15x2)
def blub(self):
return forms.ChoiceField(widget=forms.RadioSelect, choices=self.praep_button)
myfield = forms.ChoiceField(widget=forms.RadioSelect, choices=praep_button) #print --><django.forms.fields.ChoiceField object at 0x751f9b90>
def myfield_choices(self):
field = self['myfield']
"""i think here is the problem.
Above 'myfield' is a django.forms.fields.ChoiceField object, but here it is rendered to html (like it should be). I have the code from https://stackoverflow.com/questions/6766994/in-a-django-form-how-do-i-render-a-radio-button-so-that-the-choices-are-separat.
But instead i should use field = self.felder (radioselect woth tuple of the db)"""
widget = field.field.widget
attrs = {}
auto_id = field.auto_id
if auto_id and 'id' not in widget.attrs:
attrs['id'] = auto_id
name = field.html_name
return widget.render(name, field.value(), attrs=attrs)
#return widget.get_renderer(name, field.value(), attrs=attrs)
So all in all I hope the problem is clear.
If i am using AlternativForm() i get the constant form. Instead i would like to get a dynamic form. If I access in views.py:
alternative = AlternativForm(optype = op, wsgroup = ab)
alternative = alternativ.felder
than I get . Can I render that to html?
If I set in forms.py:
field = self.felder
than I get the error that it is a field and not a widget
Thank you for reading!
You just need to assign the choices in the form's __init__() method. Almost what you're doing, but instead of defining self.felder to be a field, you need to use the already initialised form's fields:
myfield = forms.ChoiceField(widget=forms.RadioSelect, choices=praep_button)
def __init__(self, optype, wsgroup, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['myfield'].choices = self.get_choices(optype, wsgroup) # create your choices in this method
def get_choices(optype, wsgroup):
# call your other methods here
return praep_button

not able to save data in django form

I am getting error Cannot assign "'1'": "dropdown.drp1" must be a "basedrop" instance. I am sharing my code. Kindly help. I got some solutions on stack but I did not understand how to implement that in my case. Django error. Cannot assign must be an instance
models.py
class basedrop(models.Model):
name = models.CharField(max_length=50,blank=False,null=False)
def __str__(self):
return self.name
class subdrop(models.Model):
name = models.CharField(max_length=100,blank=False,null=False)
bsdrop = models.ForeignKey(basedrop,null=False,blank=False,on_delete=models.CASCADE)
def __str__(self):
return self.name
class lastdrop(models.Model):
name = models.CharField(max_length=100,blank=False,null=False)
sbdrop = models.ForeignKey(subdrop,null=False,blank=False,on_delete=models.CASCADE)
def __str__(self):
return self.name
class dropdown(models.Model):
name = models.CharField(max_length=50)
drp1 = models.ForeignKey(basedrop,max_length=50,on_delete=models.CASCADE)
drp2 = models.ForeignKey(subdrop,max_length=50,on_delete=models.CASCADE)
drp3 = models.ForeignKey(lastdrop,max_length=50,on_delete=models.CASCADE)
def __str__(self):
return self.name
views.py
def create_drop(request):
if request.method == 'POST':
form = dropdownForm(request.POST or None)
if form.is_valid():
form = dropdown(name=request.POST.get('name'),drp1_Id=int(request.POST.get('drp1')),
drp2_Id=int(request.POST.get('drp2')),drp3_Id=int(request.POST.get('drp3')))
form.save()
return HttpResponse('<p>this is working</p>')
form = dropdownForm()
return render(request,'drop.html',{'form':form})
forms.py
class dropdownForm(forms.ModelForm):
drp1 = forms.ChoiceField(choices=((bs.get('id'),bs.get('name')) for bs in basedrop.objects.all().values('id','name')))
class Meta:
model = dropdown
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['drp2'].queryset = subdrop.objects.none()
self.fields['drp3'].queryset = lastdrop.objects.none()
if 'drp1' in self.data:
try:
country_id = int(self.data.get('drp1'))
self.fields['drp2'].queryset = subdrop.objects.filter(id=country_id).order_by('name')
except (ValueError, TypeError):
pass
elif 'drp2' in self.data:
try:
country_id = int(self.data.get('drp2'))
self.fields['drp3'].queryset = lastdrop.objects.filter(id=country_id).order_by('name')
except (ValueError, TypeError):
pass
elif self.instance.pk:
self.fields['drp2'].queryset = self.instance.drp1.city_set.order_by('name')
self.fields['drp3'].queryset = self.instance.drp2.city_set.order_by('name')
I don't know if it could cause failures but you're passing the POSTed argument 'drp1' as integer for drp1_Id, drp2_Id and drp3_Id.
You'd have it much easier if you choose a more intuitive coding style.
For example this line:
form = dropdown(name=request.POST.get('name'), drp1_Id=int(request.POST.get('drp1')), drp2_Id=int(request.POST.get('drp1')), drp3_Id=int(request.POST.get('drp1')))
If you get the objects and pass them to the dropdown you gain readability specially if there is an error:
drp1_pk = request.POST.get('drp1')
drp1 = basedrop.objects.get(pk=drp1_pk)
drp2 = subdrop.objects.get(pk=drp1_pk)
drp3 = lastdrop.objects.get(pk=drp1_pk)
form = dropdown(name=request.POST.get('name'), drp1=drp1, drp2=drp2, drp3=drp3)
But again:
It looks strange to pass the same primary key to three different models.

Access missing value in form.cleaned_data

I was trying to dynamically generate fields as shown in http://jacobian.org/writing/dynamic-form-generation/. My case slightly differs in that I am looking to use multiplechoicefield that is dynamically created. This is what I came up with...
views.py
def browseget(request):
success = False
if request.method == 'POST':
list_form = ListForm(request.POST)
if list_form.is_valid():
success = True
path = list_form.cleaned_data['path']
minimum_size = list_form.cleaned_data['minimum_size']
follow_link = list_form.cleaned_data['follow_link']
checkboxes = list_form.cleaned_data['checkboxes']
....do something
else:
list_form = ListForm(name_list)
ctx = {'success': success, 'list_form': list_form, 'path': path, 'minimum_size': minimum_size}
return render_to_response('photoget/browseget.html', ctx, context_instance=RequestContext(request))
forms.py
class ListForm(forms.Form):
path = forms.CharField(required=False)
minimum_size = forms.ChoiceField(choices=size_choices)
follow_link = forms.BooleanField(required=False, initial=True)
def __init__(self, *args, **kwargs):
name_list = kwargs.pop('name_list', None)
super(ListForm, self).__init__(*args, **kwargs)
print 'Received data:', self.data
if name_list:
name_choices = [(u, u) for u in name_list]
self.fields['checkboxes'] = forms.MultipleChoiceField(required=False, label='Select Name(s):', widget=forms.CheckboxSelectMultiple(), choices=name_choices)
def clean_path(self):
cd = self.cleaned_data
path = cd.get('path')
if path == '': path = None
return path
def clean_minimum_size(self):
cd = self.cleaned_data
minimum_size = cd.get('minimum_size')
if minimum_size is None: minimum_size = 0
return int(minimum_size)
The form generates and displays perfectly... until I post some data. The 'checkboxes' field doesn't show up in list_form.cleaned_data.items() while it shows in self.data. As it is the form breaks with a KeyError exception. So Im asking, how do i access the checkboxes data?
You're not passing in the name_list parameter when you re-instantiate the form on POST, so the field is not created because if name_list is False.

how to work with modelform and multiwidget

Newbie to all this! i'm working on displaying phone field displayed as (xxx)xxx-xxxx on front end.below is my code. My question is 1. all fields are mandatory, for some reason,phone is not behaving as expected.Even if it is left blank its not complaining and 2.how can i test this widget's functionality
class USPhoneNumberWidget(forms.MultiWidget):
def __init__(self,attrs=None):
widgets = (forms.TextInput(attrs={'size':'3','maxlength':'3'}),forms.TextInput(attrs={'size':'3','maxlength':'3'}),forms.TextInput(attrs={'size':'3','maxlength':'4'}))
super(USPhoneNumberWidget,self).__init__(widgets,attrs=attrs)
def decompress(self, value):
if value:
val = value.split('-')
return [val[0],val[1],val[2]]
return [None,None,None]
def compress(self, data_list):
if data_list[0] and data_list[1] and data_list[2]:
ph1 = self.check_value(data_list[0])
ph2 = self.check_value(data_list[1])
ph3 = self.check_value(data_list[2])
return '%s''%s''%s' %(ph1,ph2,ph3)
else:
return None
def check_value(self,val):
try:
if val.isdigit():
return val
except:
raise forms.ValidationError('This Field has to be a number!')
def clean(self, value):
try:
value = re.sub('(\(|\)|\s+)','',smart_unicode(value))
m = phone_digits_re.search(value)
if m:
return u'%s%s%s' % (m.group(1),m.group(2),m.group(3))
except:
raise ValidationError('Phone Number is required.')
def value_from_datadict(self,data,files,name):
val_list = [widget.value_from_datadict(data,files,name+'_%s' %i) for i,widget in enumerate(self.widgets)]
try:
return val_list
except ValueError:
return ''
def format_output(self,rendered_widgets):
return '('+rendered_widgets[0]+')'+rendered_widgets[1]+'-'+rendered_widgets[2]
class CustomerForm(ModelForm):
phone = forms.CharField(required=True,widget=USPhoneNumberWidget())
class Meta:
model = Customer
fields = ('fname','lname','address1','address2','city','state','zipcode','phone')
In models blank and null are not true.
Any input it highly appreciated.Thanks
Here is the phone field:
phone = forms.CharField(label = 'Phone',widget=USPhoneNumberWidget()
class USPhoneNumberWidget(forms.MultiWidget):
"""
A widget that splits phone number into areacode/next3/last4 with textinput.
"""
def __init__(self,attrs=None):
widgets = (forms.TextInput(attrs={'size':'3','maxlength':'3'}),forms.TextInput(attrs={'size':'3','maxlength':'3'}),forms.TextInput(attrs={'size':'4','maxlength':'4'}))
super(USPhoneNumberWidget,self).__init__(widgets,attrs=attrs)
def decompress(self, value):
if value:
val = value
return val[:3],val[3:6],val[6:]
return None,None,None
def compress(self, data_list):
if data_list[0] and data_list[1] and data_list[2]:
return '%s''%s''%s' %(data_list[0],data_list[1],data_list[2])
else:
return None
def value_from_datadict(self,data,files,name):
val_list = [widget.value_from_datadict(data,files,name+'_%s' %i) for i,widget in enumerate(self.widgets)]
if val_list:
return '%s''%s''%s' %(val_list[0],val_list[1],val_list[2])
def format_output(self,rendered_widgets):
return '( '+rendered_widgets[0]+' )'+rendered_widgets[1]+' - '+rendered_widgets[2]
But depending on how you store the phone# in db 'return' line is to be changed. here I'm accepting it as (xxx)-xxx-xxxx format.In compress it receives ph_0(areacode),ph_1(next 3),ph_2(last4) in that order.but I'm storing it as xxxxxxxxxx.
Firebug helped me understand better about what return values should be. I'll update the answer when i come to know how testing could be done.