Problems raising a ValidationError on a Django Form - python

I'm trying to validate that a submitted URL doesn't already exist in the database.
The relevant parts of the Form class look like this:
from django.contrib.sites.models import Site
class SignUpForm(forms.Form):
# ... Other fields ...
url = forms.URLField(label='URL for new site, eg: example.com')
def clean_url(self):
url = self.cleaned_data['url']
try:
a = Site.objects.get(domain=url)
except Site.DoesNotExist:
return url
else:
raise forms.ValidationError("That URL is already in the database. Please submit a unique URL.")
def clean(self):
# Other form cleaning stuff. I don't *think* this is causing the grief
The problem is, regardless of what value I submit, I can't raise the ValidationError. And if I do something like this in the clean_url() method:
if Site.objects.get(domain=url):
raise forms.ValidationError("That URL is already in the database. Please submit a unique URL.")
then I get a DoesNotExist error, even for URLs that already exist in the Database. Any ideas?

django channel in IRC saved me here. The problem was that the URLField.clean() does two things I wasn't expecting:
If no URL scheme is present (eg, http://) the method prepends 'http://' to the url
the method also appends a trailing slash.
The results are returned and stored in the form's cleaned_data. So I was checking cleaned_data['url'] expecting something like example.com and actually getting http://example.com/. Suffice to say, changing my clean_url() method to the following works:
def clean_url(self):
url = self.cleaned_data['url']
bits = urlparse(url)
dom = bits[1]
try:
site=Site.objects.get(domain__iexact=dom)
except Site.DoesNotExist:
return dom
raise forms.ValidationError(u'That domain is already taken. Please choose another')

I do it this way. It's slightly simpler.
try:
a = Site.objects.get(domain=url)
raise forms.ValidationError("That URL is already in the database. Please submit a unique URL.")
except Site.DoesNotExist:
pass
return url

I think, you can return '' and fill _errors.
msg = u"That URL is already in the database. Please submit a unique URL."
self._errors["url"]=ErrorList([msg])
return ''
or
from django.contrib.sites.models import Site
class SignUpForm(forms.Form):
# ... Other fields ...
url = forms.URLField(label='URL for new site, eg: example.com')
def clean_url(self):
url = self.cleaned_data['url']
try:
a = Site.objects.get(domain=url)
raise forms.ValidationError("That URL is already in the database. Please submit a unique URL.")
except Site.DoesNotExist:
return url
return ''
def clean(self):
# Other form cleaning stuff. I don't *think* this is causing the grief

Well I logged in cause I found this via Google with a similar issue and wanted to add a comment to Carl Meyers post noting that using self._errors is totally valid as per the Django docs:
http://docs.djangoproject.com/en/1.2/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other

Related

Django. Show error message on current page

Help me please! I'm trying to show error message:
In forms:
def clean_form(self):
url = self.cleaned_data['text']
if url == 'qwe':
raise ValidationError("Error")
return self.cleaned_data
In view:
def main_site(request):
if request.method == 'POST':
form = Form(request.POST or None)
if form.is_valid():
form.clean_form()
link = form.cleaned_data['text']
...
But when I send 'qwe' in form:
And press 'send'. Take:
But I want to see Error on same page. What's I should be do?
Thank you!
You are only checking the text field, therefore you should override the clean_text method.
def clean_text(self):
url = self.cleaned_data['text']
if url == 'qwe':
raise ValidationError("Error")
return url
Remove the clean_form() line from your code. Django will automatically call the clean_text method for you when you check if form.is_valid().
See the docs on cleaning a specific field for more info.
If you were checking multiple fields at the same time, then you would override clean. See the docs on cleaning fields that depend on each other for more information about that.

Django: forbid to add the same data for user

I have app where user can add url to favorite in database with ManyToMany relationship. So every user can have a lot of urls and every url can have a lot of users. I have problem with creating restriction to avoid adding the same url for user. I mean I want to create the mechanism where url can be added a lot of time for users, but only one per user.
In my models I have:
from django.db import models
from django.contrib.auth.models import User
class Repository(models.Model):
url = models.CharField(max_length=250,unique=False)
user = models.ForeignKey(User,related_name='user',on_delete=models.CASCADE,default='')
repositorys = models.ManyToManyField(User,related_name='user')
And in my views:
def favorites(request):
url = request.session.get('url')
repo = Repository(url=url,user=request.user)
repo.save()
repo.repositorys.add(request.user)
user = User.objects.get(username=request.user.username)
repos = user.users.all()
return render(request, 'favorites.html',{'url':url,'repos':repos})
Favorites function is called on clicked button in my template. When I click the above function is executed and redirect to /favorites. The problem is, when I click again on button and Im logged as the same user, this url is added again to database. Same problem with refreshing favorites.html. Is there any logic way to solve this problem?
EDIT:
view favorites:
def favorites(request):
url = request.session.get('url')
try:
repo = Repository(url=url,user=request.user)
repo.save()
repo.repositorys.add(request.user)
user = User.objects.get(username=request.user.username)
except IntegrityError as e:
return render_to_response('base.html')
repos = user.users.all()
return render(request, 'favorites.html', {'url': url, 'repos': repos})
My idea here was to return all urls for user then move adding to try block. When there is a IntegrityError then move to base.html and later will display some messages
Use unique_together in your model to manage uniqueness at the database level
class Repository(models.Model):
url = models.CharField(max_length=250,unique=False)
user = models.ForeignKey(User,related_name='user',on_delete=models.CASCADE,default='')
class Meta:
unique_together = [['url', 'user']]
then when you want to add a URL for a specific user, handle unique error to prevent a user to add a specific URL twice or more.
try:
# add a Repository
except IntegrityError:
# raise an error

Saving data from a modelform Django

Now heads up! I am fresh noob off the NOOB-BUS from NOOBSVILLE!
So i am workin on a form to load up information and edit that form information and im in a headache. so i am using:
Django: 1.8
Pyhton: 3.5.1
backend is sqlite
I am using a form.ModelForm to load information into but when it comes to saving this is where i am stuck. the documentation is very confusing should i use all or just one clean.
this is the forms.py
class EditContact(forms.ModelForm):
class Meta:
model = Contact
#the list of all fields
exclude = ['date_modified']
def clean(self):
if self.date_of_entry is None:
print("looking to see what works")
self.date_of_entry = datetime.date.today()
return
def clean_ContactID(self):
#see this line below this comment i dunno what it does
ContactID= self.cleaned_data.get('ContactID')
print ("cleaning it")
# i also dont know what validation code suppose to look like
# i cant find any working examples of how to clean data
return ContactID
now there are mainly more def clean_methods but i think what i want to use is clean which should use all but in my view.
this is in view.py
def saveContactInfo (request):
#this part i get
if request.user.is_authenticated():
ContactID= request.POST['ContactID']
a = ListofContacts.objects.get(ContactID=ContactID)
f = EditContact(request.POST,instance=a)
print("plz work!")
if f.is_valid():
f.save()
return render (request,"Contactmanager/editContact.html", {'contactID': contactID})
else:
return HttpResponse("something isnt savin")
else:
return HttpResponse("Hello, you shouldnt ")
and this is model.py
def clean(self):
if self.ConactID is None:
raise ValidationError(_('ContactID cant be NULL!'))
if self.date_of_entry is None:
print("think it might call here first?")
self.date_of_entry = datetime.date.today()
print ( self.date_of_entry )
if self.modified_by is not None:
self.modified_by="darnellefornow"
print(self.modified_by )
if self.entered_by is not None:
self.entered_by = "darnellefornow"
print(self.entered_by )
ContactID = self.cleaned_data.get('ContactID')
return
now above the model has the fields and the types which all have blank = true and null = true except for the excluded field date_of_entry
and ive gotten to find out that when calling is_valid() in views it calls the models.clean() but it fails to save!!! and i dont know why! i dont know how to do the validation. i would like to know the process and what is required and even an example of form validation a field.
I think you're wanting info/answers on a couple of things here, looking at your code comments. Hopefully this helps:
1) You only need to use the clean_FIELDNAME functions if you need to handle something custom specifically for that field. The Django docs show this as an example:
def clean_recipients(self):
data = self.cleaned_data['recipients']
if "fred#example.com" not in data:
raise forms.ValidationError("You have forgotten about Fred!")
# Always return the cleaned data, whether you have changed it or
# not.
return data
So in that block, they are checking to see if the email list provided contains a particular email.
2) That also shows another question you asked in your comments about how to handle the validation. You'll see in that snippet above, you could raise a forms.ValidationError. This is discussed more here: https://docs.djangoproject.com/en/1.10/ref/forms/validation/
So, if an error is raised in any of those clean_ methods or in the main clean method, the form.is_valid() will be false.
Does that help?

How do you make a url that passes a parameter work in Django?

I'm working on registration logic and I can't seem to make the parameter pass in to work properly. The error I get is a 404 page not found. Previously, I also got a “The view didn't return an HttpResponse object" error. Any help is appreciated.
Here is my url from urls.py:
url(r'^accounts/confirm/(?P<activation_key>\d+)/$', 'mysite.views.confirm', name='confirm'),
This is my views.py:
def confirm(request, activation_key):
if request.user.is_authenticated():
HttpResponseRedirect('/home')
user = Hash.objects.filter(hash_key = activation_key)
if user:
user = Hash.objects.get(hash_key = activation_key)
user_obj = User.objects.get(username= user.username)
user_obj.is_active = True
user_obj.save()
HttpResponseRedirect('/home')
I send the url with a string that looks like:
"Click the link to activate your account http://www.example.com/accounts/confirm/%s" % (obj.username, activation_key)"
So the link looks like this:
http://www.example.com/accounts/confirm/4beo8d98fef1cd336a0f239jf4dc7fbe7bad8849a127d847f
You have two issues here:
Remove the trailing / from your pattern, or make it /? so it will be optional.
/d+ will only match digits, and your link also contains other characters. Try [a-z0-9]+ instead.
Complete pattern:
^accounts/confirm/(?P<activation_key>[a-z0-9]+)$
Remove / from end of your url:
url(r'^accounts/confirm/(?P<activation_key>\d+)$', 'mysite.views.confirm', name='confirm'),
or add / to end of your link:
http://www.example.com/accounts/confirm/4beo8d98fef1cd336a0f239jf4dc7fbe7bad8849a127d847f/

How to search a string with the url patterns in django?

I would like to create a profile page for every account created. Once created the user profile can be accessed like
http://example.com/username
But before creation I should validate that the url is already available and not taken by existing url patterns. For ex: There will be a page like
http://example.com/about
Now "about" is not a valid user. But it is a valid url pattern. I should be able to prevent a user creation with a name 'about'. For that I need to check the url patterns in addition to check whether a user with that name already exists. How to do this?
An easy way out would be to have a url pattern like the following for the profile page:
http://example.com/user/username
But I've a strong requirement to have profile page like the following
http://example.com/username
You can simply try to resolve the address to a view:
from django.core.urlresolvers import resolve
from myapp.views import user_profile_view
try:
my_view = resolve("/%s/" % user_name)
if my_view == user_profile_view:
# We match the user_profile_view, so that's OK.
else:
# oops, we have another view that is mapped on that URL
# you already have something mapped on this address
except:
# app doesn't have such path
EDIT:
you can also make the check in a different way:
def user_profile_view(request, user_name):
# some code here
user_profile_view.name = "User Profile View"
and then the check above could be:
if getattr(my_view, "name", None) == "User Profile View":
...
you can add custom form field validation. Look at this post.
Django and Custom Form validation
raise forms.ValidationError(u'please choose another username')
check and raise errors.
Or you can try setting the following as url for your users,
example.com/user/<username>/
edit 1 :
you can use this as a list of all invalid usernames
I don't think you can check this with django.core.urlresolvers.resolve. Note that it just checks for the pattern for fixed url and not variable part of your url. In your case, you will most likely have 'username' as variable parameter that is passed to view. Pattern for this will match for non-existing username also. So the checking patterns is not good solution.
Better method will be separating out static or other pages in different namespace. e.g. http://example.com/static/about
Or you can have predefined keywords/reserved words for your site e.g. [ "about", ... ] and check it against username while creating user.
Put your username view at the end of urls.py, so that other url rules will be checked first.
Then the easiest way is to have a list of invalid user names which should be used in user registration validation.
def clean_username(self):
INVALID_USERNAMES = ('about', 'admin', 'register', '...')
username = self.cleaned_data['username']
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
pass
else:
raise forms.ValidationError(u'%s already exists' % username )
if username in INVALID_USERNAMES:
raise forms.ValidationError(u'%s is not a valid username' % username )
return username
firstly, a url rule for usernames:
url(r'^(?P<username>[-\w]+)/$', 'membership.views.profile', name='profile'),
making sure that a username doesn't conflict with an existing url rule is a little harder.
the way I usually handle this is by adding uniqueness to the url:
url(r'^user/(?P<username>[-\w]+)/$', 'membership.views.profile', name='profile'),
if you absolutely must have the url for profiles start with the username then you can try to rake the urls using a method like this one: https://stackoverflow.com/a/2094270/884453 and then make sure that username is both unique against other usernames and against routes
EDIT
as i was writing this someone posted a cool idea for a validator that makes a bunch more sense.
using from django.core.urlresolvers import resolve to check it for uniqueness is a great solution
from django.core.exceptions import ValidationError
from django.core.urlresolvers import resolve
def validate_unique_resolve(value):
urlroute = True
try:
urlroute = resolve(path, urlconf)
except urlresolvers.Resolver404:
urlroute = False
if urlroute != False :
raise ValidationError(u'%s is a reserved url pattern' % value)

Categories