Uniqueness validation in django form (Both ADD and EDIT) - django

Here, I wanted to achieve mobile number uniqueness validation in both conditions i.e. at the time of ADD and EDIT form details.
I am facing a problem in form edit mode because I am unable to perform uniqueness validation in form edit submit.
I wanted only a single mobile no to be stored in a database. While I am editing the form it is bypassing my logic and allowing duplicate entries from EDIT.
Could you guys suggest any solution for uniqueness validation?
forms.py
class StudentForm(forms.Form):
name = forms.CharField(required=True,max_length=10)
city = forms.CharField(required=True,max_length=10)
mobile_no = forms.CharField(required=True, max_length=10)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(StudentForm, self).__init__(*args, **kwargs)
def clean_mobile_no(self):
mobile_no = self.cleaned_data.get('mobile_no')
id = self.request.POST.get('id')
if stu_id:
if Student.objects.filter(mobile_no=mobile_no).count()>1:
raise forms.ValidationError("You have already added mobile no.")
else:
return mobile_no
elif mobile_no and Student.objects.filter(mobile_no=mobile_no).exists():
raise forms.ValidationError("Alreadsy exists.")
return mobile_no
views.py
def add_edit_(request, stu_id=None):
if stu_id:
stu = Student.objects.get(pk=stu_id)
if request.method == 'POST':
form = StudentForm(request.POST, request=request)
if form.is_valid():
name = request.POST['name ']
city = request.POST['ccity']
mobile_no = form.cleaned_data['mobile_no']
if stu_id:
stu.name=name
stu.ccity=city
stu.mobile_no=mobile_no
cstu.save()
messages.success(request, 'Edit Successfully...')
return redirect('/.../')
else:
p = Student(name=name, city=city, mobile_no=smobile_no
)
p.save()
messages.success(request, 'Information added Successfully...')
return redirect('/../')
elif id:
form = CStudentForm(initial={'name':stu.name,
'city':stu.city,'mobile_no':std.mobile_no '})
else:
form = StudentForm()
context = {
'form': form,
'stu_id':stu_id,
}
return render(request, 'student.html', context=context)
Thank you in advance.

Related

ValueError at --View didn't return an HttpResponse object. It returned None instead

I got this error when submitting the form.
ValueError at /en/checkout/ The view core.views.EnCheckoutView didn't
return an HttpResponse object. It returned None instead.
I double-checked the views.py
Sometimes after deleting the database file (db.sqlite3): it worked correctly with the same views.py and html template, but after restart computer, the same error comes back again.
and here is its views.py
class EnCheckoutView(View):
def get(self, *args, **kwargs):
try:
order = Order.objects.get(user=self.request.user, ordered=False)
form = CheckoutForm()
context = {
'form': form,
'couponform': CouponForm(),
'order': order,
'DISPLAY_COUPON_FORM': True
}
shipping_address_qs = Address.objects.filter(user=self.request.user, address_type='S', default=True)
if shipping_address_qs.exists():
context.update({
'default_shipping_address': shipping_address_qs[0]
})
billing_address_qs = Address.objects.filter(user=self.request.user, address_type='B', default=True)
if billing_address_qs.exists():
context.update({
'default_billing_address': billing_address_qs[0]
})
return render(self.request, 'en-checkout-page.html', context)
except ObjectDoesNotExist:
messages.info(self.request, 'You do not have an active order.')
return redirect('core:en-checkout')
def post(self, *args, **kwargs):
form = CheckoutForm(self.request.POST or None)
try:
order = Order.objects.get(user=self.request.user, ordered=False)
if form.is_valid():
use_default_shipping = form.cleaned_data.get("use_default_shipping")
if use_default_shipping:
print('Using the default shipping address')
address_qs = Address.objects.filter(user=self.request.user, address_type='S', customer_name=customer_name, phone=phone, email=email, default=True)
if address_qs.exists():
shipping_address = address_qs[0]
order.shipping_address = shipping_address
order.save()
else:
messages.info(self.request, 'No default shipping address available')
return redirect('core:en-checkout')
else:
print('User is entering a new shipping address')
customer_name = form.cleaned_data.get('customer_name')
phone = form.cleaned_data.get('phone')
email = form.cleaned_data.get('email')
shipping_address1 = form.cleaned_data.get('shipping_address')
shipping_address2 = form.cleaned_data.get('shipping_address2')
shipping_country = form.cleaned_data.get('shipping_country')
shipping_zip = form.cleaned_data.get("shipping_zip")
if is_valid_form([shipping_address1, shipping_country, shipping_zip]):
shipping_address = Address(
user=self.request.user,
customer_name=customer_name,
phone=phone,
email=email,
street_address=shipping_address1,
apartment_address=shipping_address2,
country=shipping_country,
zip=shipping_zip,
address_type='S'
)
shipping_address.save()
order.shipping_address = shipping_address
order.save()
set_default_shipping = form.cleaned_data.get('set_default_shipping')
if set_default_shipping:
shipping_address.default = True
shipping_address.save()
else:
messages.info(self.request, 'Please fill in the required shipping address fields')
use_default_billing = form.cleaned_data.get('use_default_billing')
same_billing_address = form.cleaned_data.get('same_billing_address')
if same_billing_address:
billing_address = shipping_address
billing_address.pk = None
billing_address.save()
billing_address.address_type = 'B'
billing_address.save()
order.billing_address = billing_address
order.save()
elif use_default_billing:
print('Using the default billing address')
address_qs = Address.objects.filter(
user=self.request.user,
address_type='B',
default=True
)
if address_qs.exists():
billing_address = address_qs[0]
order.billing_address = billing_address
order.save()
else:
messages.info(self.request, 'No default billing address')
return redirect('core:checkout')
else:
print('User is entering a new billing address')
billing_address1 = form.cleaned_data.get('billing_address')
billing_address2 = form.cleaned_data.get('billing_address2')
billing_country = form.cleaned_data.get('billing_country')
billing_zip = form.cleaned_data.get('billing_zip')
if is_valid_form([billing_address1, billing_country, billing_zip]):
billing_address = Address(
user=self.request.user,
street_address=billing_address1,
apartment_address=billing_address2,
country=billing_country,
zip=billing_zip,
address_type='B'
)
billing_address.save()
order.billing_address = billing_address
order.save()
set_default_billing = form.cleaned_data.get('set_default_billing')
if set_default_billing:
billing_address.default = True
billing_address.save()
else:
messages.info(self.request, 'Please fill in the required billing address fields')
payment_option = form.cleaned_data.get('payment_option')
if payment_option == 'S':
return redirect('core:payment', payment_option='stripe')
elif payment_option == 'P':
return redirect('core:payment', payment_option='paypal')
else:
messages.warning(self.request, 'Invalid payment option selected')
return redirect('core:checkout')
except ObjectDoesNotExist:
messages.warning(self.request, 'You do not have an active order')
return redirect('core:en-order-summary')
I appreciate any help!!
There's waaayyyyy too much code in you post() method - you should extract all the logic to the form's save method, use proper form validation (use modelforms as much as possible), and split it into simple, readable and testable methods.
This being said, your problem is obvious as soon as you extract all the code under the if form.is_valid() - there's no else branch. So if your form doesn't validate, the function does return None indeed.
EDIT
I shall add else right before except..................., right?
You have to add it at the same level as the if form.is_valid() - now where it ends up in this plate of spaghettis is beyond my reading abilities, honestly :-/
As a general rule, consider that if you have more than about 20 lines and 2 levels of indentation in a function, then it's probably time to refactor your code. A first obvious refactoring is to move things around to reduce indentation levels. In you case, you can already start by moving the except clause right after the only place where it makes sense:
def post(self, *args, **kwargs):
try:
order = Order.objects.get(user=self.request.user, ordered=False)
except Order.DoesNotExist:
messages.warning(self.request, 'You do not have an active order')
return redirect('core:en-order-summary')
form = CheckoutForm(self.request.POST or None)
if form.is_valid():
# etc....
You already gained one level of indentation (and reduced your try block to the strict minimum so it won't be able to catch another unexpected DoesNotExist exception, should it arise in some code you're calling).
Then make proper use of form validation - here for example:
if address_qs.exists():
shipping_address = address_qs[0]
order.shipping_address = shipping_address
order.save()
else:
messages.info(self.request, 'No default shipping address available')
return redirect('core:en-checkout')
this is plain wrong and very rude to your user who will then have to retype everything again. The form itself should validate this. In fact, it shouldn't even show a "use default shipping" option if the user doesn't have a default shipping address yet.
Actually, your view code should really looks like:
def post(self, *args, **kwargs):
try:
order = Order.objects.get(user=self.request.user, ordered=False)
except Order.DoesNotExist:
messages.warning(self.request, 'You do not have an active order')
return redirect('core:en-order-summary')
form = CheckoutForm(self.request.POST, order=order)
if form.is_valid():
form.save()
# this is supposed to has been validated by the form too
payment_option = form.get_payment_option()
return redirect('core:payment', payment_option=payment_option)
# factor out all the common code creating the context
# for both get and post in a get_context_data method
context = self.get_context_data()
context["form"] = form
return render(self.request, 'en-checkout-page.html', context)
and everything else should be in your form's validation and save() method.

Complicated Django ModelForm validation with OneToOneField and customed argument

Sorry for the lengthy question. I have a complicated situation with django modelform validation. I have a model UserProject ready and created many objects. I also have another model Action_Inputs to accept multiple parameters, which is a onetoonefield relation with UserProject. I do need customed input argument for one field of Action_Inputs. But I cannot have the form valided.
models.py
class UserProject(models.Model):
pid = models.CharField(max_length=10, null=False, unique=True)
email = models.EmailField(max_length=254, null=False)
directory = models.CharField(max_length=255)
class Action_Inputs(models.Model):
userproject = models.OneToOneField(UserProject, null=False)
method = models.CharField(max_length=255)
file = models.FileField(upload_to='userdata')
Now I have the following ModelForm which takes a customed input argument jobid, catched from url, which is a string to get back to the previous UserProject pid:
class ActionInputsForm(ModelForm):
def __init__(self, jobid, *args, **kwargs):
super(ActionInputsForm, self).__init__(*args, **kwargs)
self.fields['userproject'].initial = jobid
class Meta:
model = Action_Inputs
fields = ['userproject', 'method', 'file'] # userproject will be hidden
def clean_userproject(self):
userproject = self.cleaned_data['userproject']
if len(userproject) != 10:
raise forms.ValidationError("---PID error.")
return UserProject.objects.get(pid=userproject)
def clean(self):
return self.cleaned_data
In my views.py
def parameters_Inputs(request, jobid):
if request.method == "POST":
form1 = ActionInputsForm(request.POST, request.FILES, jobid)
if form1.is_bound:
form1.save()
return render(request, 'goodlog.html', {'jobid': jobid})
elif request.method == "GET":
form1 = ActionInputsForm(jobid)
return render(request, 'inputsform.html',
{'form1': form1, 'jobid': jobid})
Now the request.POST['userproject'] is empty, which means the jobid has not been modified by init, the request.FILES looks correct but the validation is false. It says Unicode object has no attrite get, which is related to the uploaded file. Any idea about what is wrong? Thanks very much.
The following works:(thanks to Vladimir Danilov)
def __init__(self, jobid, *args, **kwargs):
super(ActionInputsForm, self).__init__(*args, **kwargs)
self.fields['userproject'].initial = UserProject.objects.get(pid=jobid)
def clean_userproject(self):
userproject = self.cleaned_data['userproject']
if not userproject:
raise forms.ValidationError("---UserProject not found.")
return userproject
def parameters_Inputs(request, jobid):
if request.method == "POST":
form1 = ActionInputsForm(jobid, request.POST, request.FILES)
.......
Not answer, but do you mean ActionInputsForm instead of Action_Inputs in these lines?
form1 = Action_Inputs(request.POST, request.FILES, jobid)
# ...
form1 = Action_inputs(jobid)
Also, you should write ActionInputsForm(jobid, request.POST, request.FILES).
Because in your case jobid will be request.POST.

Pass logged user to model field django

I don't know how to get the username from the current user.
I have a edit form rendered with djano-crispy-forms:
class RecepcionForm(forms.ModelForm):
fecha_recepcion = forms.DateField(widget=DateInput())
def __init__(self,*args,**kwargs):
super(RecepcionForm,self).__init__(*args,**kwargs)
self.helper = FormHelper(self)
self.helper.layout = Layout(
Field('id_proveedor',
'anio',
'mes',
'usuario',
readonly = True
),
Fieldset('',
'fecha_recepcion',
'num_archivos',
Submit('save','Grabar')
)
)
class Meta:
model = DetalleRecepcion
my views.py:
#login_required(login_url='/login/')
def RecepcionView(request):
idp = request.GET.get('i')
anio = request.GET.get('a')
mes = request.GET.get('m')
if request.method == 'POST':
r = DetalleRecepcion.objects.get(id_proveedor=idp,anio=anio,mes=mes)
form = RecepcionForm(request.POST, instance=r)
if form.is_valid():
form.save()
return HttpResponseRedirect('/monitor/')
else:
r = DetalleRecepcion.objects.get(id_proveedor=idp,anio=anio,mes=mes)
form = RecepcionForm(instance=r)
return render_to_response('recepcion.html',
{'form':form},
context_instance=RequestContext(request))
I need to fill the field usuario with the logged username.
I tried with form = request.user.username before the save of the form.
I am confused of this have to be done passed the value in the form definition or in the view.
If is possible to overwrite the retrieved value from the database and fill the field with the username in the form class.
Another question
How can I change the widget type in the form. The field id_proveedor is a foreign key and is rendered as a drop down box (select widget), but I need to show the value displayed in a label where the can't edit the value.
I tried with the readonly propertie, but the user is not capable to write in the select box, but is capable to select from the drop down.
How can change the widget or how can I disabled the drop dwon function from the select box
Thanks in advance
You can always pass whatever arguments or keyword arguments you need to a form class, you just have to remove them from the *args or **kwargs that are passed on when calling super(), otherwise Django will throw an exception because it's receiving an arg or kwarg it's not expecting:
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user') # notice the .pop()
super(MyForm, self).__init__(*args, **kwargs)
# views.py
def my_view(request):
# assuming the user is logged in
form = MyForm(user=request.user)
I came across the same as your problem and found a solution just now. I do not know whether this is the best solution or maybe I will have problem later.
def add_trip_event(request):
#form = Trip_EventForm()
#return render(request, 'trips/add_trip_event.html', {'form': form})
if request.method == "POST":
form = Trip_EventForm(request.POST, request.FILES)
if form.is_valid():
post = form.save(commit=False)
post.trip_owner = Owner.objects.get(owner=request.user)
post.pub_date = timezone.now()
post.view = 0
post.save()
form.save_m2m()
return HttpResponseRedirect(reverse('trips:index'))
else:
form = Trip_EventForm()
return render(request, 'trips/add_trip_event.html', {'form': form})

Django: Save id on a OneToOneField

models:
class UserDataUpdate(models.Model):
code = models.CharField(max_length=8)
address = models.CharField(max_length=50)
class UserSurvey(models.Model):
about_treatment = models.CharField(max_length=2)
user_data_update = OneToOneField(UserDataUpdate)
views:
#login_required
def generate_survey(request):
user_data_update = UserDataUpdate.objects.get(code=request.user.username)
if request.method == 'POST':
form = SurveyForm(request.POST)
if form.is_valid():
form.save()
return redirect('/success')
else:
form = SurveyForm(request.GET)
return render_to_response(
'survey.html',
{'form': form },
context_instance = RequestContext(request))
form:
class SurveyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(SurveyForm, self).__init__(*args, **kwargs)
for field in self.fields.values():
field.widget = RadioSelect(choices=SURVEY_CHOICES)
class Meta:
model = Survey
exclude = ['user_data_update']
I just need a way to set the UserDataUpdate id (that already has been created) on a UserSurvey.
I'm getting this message on generate_survey request.POST:
user_data_update_app_usersurvey.user_data_update_id may not be NULL
It should be clear to you that you get the user_data_update value but then don't do anything with it. I guess you want to set it on the object that's created by the form:
if form.is_valid():
instance = form.save(commit=False)
instance.user_data_update = user_data_update
instance.save()
(I don't understand what all that stuff in the form's __init__ method is supposed to do. You only have one field in your form, anyway.)

Why is self.instance not set in clean function for a bound form?

I have a Django 1.1 model with unique_together on the owner and title where owner is foreign key on a user. This constraint is enforced but only after the clean. According to Django docs, I should be able to access self.instance to see non-form field object properties of a model instance.
However, I get the error
'JournalForm' object has no attribute 'instance'
Why is self.instance not set on this bound form in either the form clean() or the field clean_title() methods?
My model:
class Journal (models.Model):
owner = models.ForeignKey(User, null=True, related_name='journals')
title = models.CharField(null=False, max_length=256)
published = models.BooleanField(default=False)
class Meta:
unique_together = ("owner", "title")
def __unicode__(self):
return self.title
My form:
class JournalForm (forms.Form):
title = forms.CharField(max_length=256,
label=u'Title:')
html_input = forms.CharField(widget=TinyMCE(attrs={'cols':'85', 'rows':'40'}, ),
label=u'Journal Content:')
published = forms.BooleanField(required=False)
def clean(self):
super(JournalForm, self).clean()
instance = self.instance
return self.cleaned_input
def clean_title(self):
title = self.cleaned_data['title']
if self.is_bound:
if models.Journal.objects.filter(owner.id=self.instance.owner.id, title=title).exclude(id=self.instance.id).count() > 0:
raise forms.ValidationError(u'You already have a Journal with that title. Please change your title so it is unique.')
else:
if models.LabJournal.objects.filter(owner.id=self.instance.owner.id, title=title).count() > 0:
raise forms.ValidationError(u'You already have a Journal with that title. Please change your title so it is unique.')
return title
As requested - the view code:
def journal (request):
try:
journal = models.Journal.objects.get(id=id)
if request.method == 'GET':
if request.user.is_active:
if request.user.id == journal.owner.id:
data = {
'title' : journal.title,
'html_input' : _journal_fields_to_HTML(journal.id),
'published' : journal.published
}
form = forms.JournalForm(initial=data)
return shortcuts.render_to_response('journal/Journal.html', { 'form':form, })
else:
return http.HttpResponseForbidden('<h1>Access denied</h1>')
else:
return _display_login_form(request)
elif request.method == 'POST':
if LOGIN_FORM_KEY in request.POST:
return _handle_login(request)
elif request.user.is_active and request.user.id == journal.owner.id:
form = forms.JournalForm(data=request.POST)
if form.is_valid():
journal.title = form.cleaned_data['title']
journal.published = form.cleaned_data['title'];
journal.save()
if _HTML_to_journal_fields(journal, form.cleaned_data['html_input']):
html_memo = "Save successful."
else:
html_memo = "Unable to save Journal."
return shortcuts.render_to_response('journal/Journal.html', { 'form':form, 'saved':html_memo})
else:
return shortcuts.render_to_response('journal/Journal.html', { 'form':form })
return http.HttpResponseNotAllowed(['GET', 'POST'])
except models.Journal.DoesNotExist:
return http.HttpResponseNotFound('<h1>Requested journal not found</h1>')
Well there are a couple of issues here.
First is that you're not using a ModelForm. The docs you link to are for those, not for standard forms.
Secondly, in order for the form to have an instance attribute, you need to pass that instance in when you're instantiating the form.
If you do use a ModelForm, you won't need the code that converts the journal fields to the form fields, and vice versa on save - the form does that for you. You'll also be able to remove the clean_title method which checks for uniqueness, because that's already defined by the unique_together constraint on the model, and the ModelForm will enforce that for you.