Django Integrity Error Handling - python

I have created a website for user registration with first name, last name, phone no and so on.
I have successfully established a connection to MySQL database. I want help regarding IntegrityError handling in django,since I'm a newbie.
When 2 users input same phone number, django takes me to its debug page with whole lots of information. Instead I want to notify the user then and there that another user with same phone number already exists. Please provide any pointers on this.
Following is my views.py file in which I process the form:
from django.shortcuts import render
from formProcessing.forms import UserForm
def form(request):
#This is using regular Django forms
#print request.POST
#form = EmailForm(request.POST or None)
#This is using model forms
form = UserForm(request.POST or None)
if form.is_valid():
new_instance = form.save(commit=True)
new_instance.save()
context = {"form": form }
template = "form.html"
return render(request,template,context)

In your UserForm you can overwrite clean method of your number attribute, lets say that your attribute is called 'number'
class UserForm(ModelForm):
#your user form attributes and stuff
def clean_number(self, value):
user_number = value
number_occurrences = User.objects.filter(number=user_number).count()
if number_occurrences > 0:
raise forms.ValidationError("You number is already taken by other user")
return self.cleaned_data
Check django docs about form validation
If you dont wan't to overwrite clean method and do it whitin your view. you can. (Is not elegant)
def form(request):
#This is using regular Django forms
#print request.POST
#form = EmailForm(request.POST or None)
#This is using model forms
number = request.POST.get('telephone')
number_occurrences = User.objects.filter(number=user_number).count()
if number_occurrences > 0:
context = {'error':'Number already exist'}
return render(request,template,context)
form = UserForm(request.POST or None)
if form.is_valid():
new_instance = form.save(commit=True)
new_instance.save()
context = {"form": form }
template = "form.html"
return render(request,template,context)

Before you create an object, query the db for the existence of that phone number.
if form.is_valid():
ph_number = #"Phone number from form"
if User.objects.filter(phone_number = ph_number).first():
return HttpResponse("The number already exists.")
new_instance = form.save(commit=True)
new_instance.save()
The first() method returns the index 0 of the queryset. So, if there is element 0 in the queryset, the error message will be displayed to the user.

you can probably do something with field validation, although I donĀ“t know exactly what your error is, or how the model is designed.
Anyway, you could try to look up whether the value is unique (i.e., look if another record exists with the same number), before trying to save it; this might be the cleanest.
https://docs.djangoproject.com/en/1.7/ref/forms/validation/#cleaning-a-specific-field-attribute
so something like:
def clean_telephone(self):
n = User.objects.filter(telephone=self.cleaned_data['telephone']).count()
if n > 0:
raise forms.ValidationError("telephone number is already in database")
return data
Or you could try to save it and catch the unique error with a try/except clause. This uses less databases access attempts.

Related

Pass a variable to another function and render an html template at the same time in django

We want to access the same variable in every function inside our views.py. Since it is not constant, we cannot use it as a global variable.
Is it possible to pass a variable to another function while also rendering an HTML template? What are the alternatives if none exist?
This is our login function in views.py
def loginpage(request):
errorMessage = ''
# Applicant Login
if request.method=="POST":
if request.POST.get('username') and request.POST.get('pwd'):
try:
currentUser=Applicant.objects.get(username=request.POST['username'],pwd=request.POST['pwd'])
currentUser=Applicant.objects.get(username=request.POST['username'])
first = currentUser.firstname
middle = currentUser.middleinitial
last = currentUser.lastname
AppDashboard = ApplicantDashboardPageView(currentUser, request)
except Applicant.DoesNotExist as e:
errorMessage = 'Invalid username/password!'
return render(request, 'home.html')
The currentUser variable inside our login function is the variable we want to pass in this function
def ApplicantdashboardPageView(currentUser, request):
appPeriod = ApplicationPeriod.objects.all()
exam = ExaminationSchedule.objects.all()
posts = Post.objects.all().order_by('-created_on')
form = PostForm()
name=userNaCurrent
print('from storeCurrentUser', name)
if request.method == "GET":
try:
posts = Post.objects.all().order_by('-created_on')
form = PostForm()
#applicantID=currentUser.id
#applicantNotification = Applicant.objects.get(id=applicantID)
return render(request, 'applicantdashboard.html', context={'UserName' : name, 'posts':posts, 'appPeriod':appPeriod, 'exam':exam})
except ObjectDoesNotExist:
return render(request, 'applicantdashboard.html', context={'UserName' : name, 'posts':posts,})
return render(request, 'applicantdashboard.html', context={'UserName' : name, 'posts':posts, 'appPeriod':appPeriod, 'exam':exam})
I am new to Django so please bear with me if my question seem too basic. Thank you
Store raw user password is a very big flaw in security. Please read more about Django Authentication system https://docs.djangoproject.com/en/4.1/topics/auth/
Basically, to store critical confidential information like passwords you need to at least, encrypt it. But for passwords you don't need to see the raw value of it, isn't it? Therefore, you just need to hash it and compare it every time you need to authenticate the user. Read more here Best way to store password in database
Django Auth system will also help to solve the issue by injecting the current user into a "global" request object so that you can access it everywhere.
You can do the same by keeping those 2 methods in a class and accessing variables by creating objects for it.

How can i fetch my data from database to django template?

Whenever I run this,
Exception Value:
name 'current_user' is not defined;
error is raised.
I am not getting where i am doing the mistake as I m new in django programming. Please help me fetch the data
# To add a new product in the database
def AddNewProduct(request):
if request.method == "POST":
current_user = request.user
product_title =request.POST['product_title']
uid = request.POST['uid']
specification =request.POST['specification']
sale_price = request.POST['sale_price']
discount = request.POST['discount']
img1 = request.FILES['img1']
img2 = request.FILES['img2']
promote_method = request.POST['promote_method']
terms_conditions = request.POST['terms_conditions']
newproduct = AffProduct(user_id=current_user.id, product_title=product_title, uid=uid, specification=specification, sale_price=sale_price,
discount=discount, img1=request.FILES.get('img1'), img2=request.FILES.get('img2'),
promote_method=promote_method, terms_conditions=terms_conditions)
newproduct.save()
# Status message
messages.success(request, 'Product added successfully')
return render(request, 'blink_network.html')
else:
return render(request, 'blink_network.html')
#Here i m trying to fetch my data.
def showproduct(request):
if request.user.is_authenticated:
result = AffProduct.objects.filter(user_id=current_user.id)
else:
result = AffProduct.objects.all()
return render(request, 'blink_viewproduct.html', {'result': result})
It looks like you will be getting that problem from showproduct(request) because you don't define current_user in that method before calling it.
to call this
result = AffProduct.objects.filter(user_id=current_user.id)
you need to define current_user = request.user beforehand
Could you share the relevant models.py file as well? You probably linked the user model with the ForeignKey with the Product model. If you did this, you need to give current_user, not current_user.id, django handles the matching itself.
Also, I guess you are using django form. If you are using it, I recommend you to use it because you can increase the readability of your code by writing less code.

"save and add another" in Django (not admin): submit then pre-populate one field of form

I have a form, "results", where one of the fields, "subjectID", is many-to-many because there's more than one result for each subject. I want one of the submit buttons to let me save what I've entered, then redirect to the same form, now unbound except that the many-to-many "subjectID" field stays the same so I can enter more results for that subject.
Edit: I should have made it clear that I wanted the instance that I had selected in the subjectID field to stay the same. I posted the code below that actually seems to be working for me
from models.py
class ResultsForm(forms.Modelform):
class Meta:
model = models.Results
fields = ['subjectID', # this is the field want
# to populate the form with when I "save and add another"
'slideNum', # IntegerField
'resultType' ] # ForeignKey
from views.py
def addResults(request):
if request.method == 'POST'
form = ResultsForm(request.POST)
if form.is_valid():
form.save()
if 'Save_and_add_another' in request.POST:
subjectID = form.fields['subjectID']
prepop = {'subjectID' : subjectID}
form = ResultsForm(initial=prepop)
return render(request, 'slideAdmin/addResults.html', {'form': form})
elif 'Save_and_return' in request.POST:
return HttpResponseRedirect('/home/')
else:
form = ResultsForm()
return render(request, 'slideAdmin/addResults.html', {'form': form})
Right now when I click on "save and add another" from my addResults form, I get this error:
TypeError at /slidebox/addResults
'ModelMultipleChoiceField' object is not iterable
which happens when rendering {{ form.as_p }} in the template.
Edit: Changes I made to views.py
if 'Save_and_add_another' in request.POST:
subjectID = form.cleaned_data.get('subjectID')
form = ResultsForm(initial={'subjectID': subjectID})
return render(request, 'slideAdmin/addResults.html', {'form': form})
As far as I can tell, this change works. Thanks again
You should always use form.cleaned_data.get('subjectID') versus pulling the field directly from the post data. You need to pass in a list of the pk's for the M2M field.
Your view can also use a touch of cleanup:
from django.core.urlresolvers import reverse
def addResults(request):
form = ResultsForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
form.save()
if 'Save_and_add_another' in request.POST:
subjectID = form.cleaned_data.get('subjectID', [])
if subjectID:
subjectID = subjectIDs.split(',')
form = ResultsForm(initial={'subjectID': subjectID})
elif 'Save_and_return' in request.POST:
return HttpResponseRedirect(reverse('home')) # don't hard code
return render(request, 'slideAdmin/addResults.html', {'form': form})
I'm not sure if you will be able to keep the form unbound when initialized.
Your form.fields is an ordered dict of django.forms.fields objects. You just want the ids, and not all the other info that comes across it.
Get the data straight from the POST dictionary.
subjectID = request.POST.get('subjectID', '')
If this is a true many to many model. You need to make sure the data is setup correctly for the initialization.
# We have to special-case M2Ms as a list of comma-separated PKs.
if isinstance(f, models.ManyToManyField):
initial[k] = initial[k].split(",")
Here is the initialization method from the django source code for Admin (or as I call it my super detailed and complicated Django cheat sheet, I am pedantic)
def get_changeform_initial_data(self, request):
"""
Get the initial form data.
Unless overridden, this populates from the GET params.
"""
initial = dict(request.GET.items())
for k in initial:
try:
f = self.model._meta.get_field(k)
except FieldDoesNotExist:
continue
# We have to special-case M2Ms as a list of comma-separated PKs.
if isinstance(f, models.ManyToManyField):
initial[k] = initial[k].split(",")
return initial
Some PEP8 nonsense as well
classes are camel case ex: class MyAwesomeClass(object):
everything else is lower with underscores. ex: awesome_id = awesome1245
Good Luck!!

Why Django test with blank data post does not work?

I'd like to understand why this testing case does not work: I'm testing that my signup form in my view returns errors when I try to submit an empty form.
In tests.py:
class SignupViewTestCase(TestCase):
def test_signup_post_blank(self):
resp = self.client.post(reverse(signup), {}) # blank data dictionary
self.assertFormError(resp, form='signup_form', field='email',
errors='Ce champ est obligatoire') # French version of "This field is mandatory"
In views.py:
def signup(request):
signup_form = SignupForm(request.POST or None)
if signup_form.is_valid():
ema = signup_form.cleaned_data['email']
raw_pwd = signup_form.cleaned_data['password']
try:
BizProfile.create(ema, raw_pwd)
except IntegrityError:
signup_form.errors['__all__'] = signup_form.error_class([
ERR_USER_EXISTS])
else:
messages.success(request, SUC_ACC_CREA)
messages.info(request, INF_CONN)
return redirect(signin)
return render(request, 'sign_up.html', locals())
When testing manually in my browser, I can see there IS actually an error on the email field when I submit it without data.
But test result says:
AssertionError: The field 'email' on form 'signup_form' in context 0 contains no errors
Any idea of what is happening? Thanks.
Actually, the problem is related to the or None.
That's because an empty dictionary is false. In an "or" condition, Python always returns the second value if the first is false. That means your form is being instantiated just with "None", rather than an empty dictionary: which means it is not being bound at all. A non-bound form does not have any errors.
Changing your test is not a good solution, because a browser would never submit the "email" key without a value: fields without values are simply not send in the POST data, which is why an empty dictionary is the right way to test this. Instead of changing your test, you should use the canonical view pattern, and remove that broken shortcut.
if request.method == 'POST':
signup_form = SignupForm(request.POST)
if signup_form.is_valid():
...
else:
signup_form = SignupForm()
return...

Django ModelForm Submit To Database

When I load my view at : localhost:8000/Scan, it throws an issue of:
TypeError on views.py in Scan, line 27:
form = Scan() # Otherwise, set the form to unbound
Any idea what I'm doing wrong here? I tried researching, but couldn't find the answer. (Django newbie here) . Thank you all!
Views.py
from django.http import HttpResponse
from Scanner.forms import SubmitDomain
def Scan(request):
if request.method == 'POST': # If the form has been submitted...
form = SubmitDomain(request.POST) # A form bound to the POST data
if form.is_valid(): # If form input passes initial validation...
form.cleaned_data['domainNm'] ## clean data in dictionary
try:
## check if Tld Table has submitted domain already
from Scanner.models import Tld
Tld.objects.get(domainNm=form.cleaned_data['domainNm'])
except Tld.DoesNotExist:
print "Would you like to create an account?"
## redirect to account creation
else:
print "Do you have an account? Please login."
## redirect to account login
else:
form = Scan() # Otherwise, set the form to unbound
Forms.py
from django.forms import ModelForm
from Scanner.models import Tld
class SubmitDomain(ModelForm):
class Meta:
model = Tld #Create form based off Model for Tld
fields = ['domainNm',]
def clean_domainName(self):
val = self.clean_domainName('domainNm')
return val
## This creates the form.
form = SubmitDomain()
In your model form:
from django.forms import ModelForm
from Scanner.models import Tld
class SubmitDomainForm(ModelForm):
class Meta:
model = Tld
fields = ['domainNm']
def clean_domainName(self):
val = self.cleaned_data.get('domainNm')
if Tld.objects.filter(domainNm=val).count() > 0:
raise forms.ValidationError(u'Sorry that domain already
exists, etc, etc')
return val
In your view, do:
from django.shortcuts import render
from Scanner.forms import SubmitDomainForm
def scan(request): # functions should start with a lowercase letter
# Bind the post data to the form, if it exists.
# No need for a separate if statement here
form = SubmitDomainForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
# save your model form, or do something else
return render(request, 'your-template.html', {'form': form})
Hope that helps you out. Your view is currently instantiating the wrong type of object for the form, hence the TypeError. Your current clean method on your model form will never validate anything. It just sets the value equal to the clean function. Instead of cluttering your view with form validation logic, put that into the clean method of the form for that field and you can raise exceptions for different conditions.
it fails when reuqest.method != "POST", in which case form is not defined
The problem is not specific to django, it's basic python. Your indentation is wrong. The code should probably look like this:
if request.method == 'POST':
form = SubmitDomain(request.POST)
if form.is_valid(): # indent fixed here
form.cleaned_data['domainNm']

Categories