Django forms URLField allow empty scheme - django

I have a Django form that uses a 'forms.URLField' like local_url1 = URLField(label="First Local URL", required=False). If a user inputs something like 'https://www.google.com' then the field validates without error.
However, if the user puts 'www.google.com' the field fails validation and the user sees an error. This is because the layout of a URL is scheme://host:port/absolute_path and the failing URL is missing the scheme (e.g. https), which Django's URLFieldValidation expects.
I don't care if my users include the scheme and nor should my form. Unfortunately, the error from django is completely useless in indicating what is wrong, and I've had multiple users ask why it says to enter a valid URL. I'm also certain I've lost paying customers because of this.
Is there a way to have all the other validation of a URL take place, but ignore the fact that the scheme is missing? At the very least, can I change the error message to add something like "Did you include http?". I've attempted implementing my own URLField and URLFieldValidation, but unless that's the path I have to take, then that is a different StackOverflow question.
I'm using Django 1.7, by the way. Thanks for any help!

URL/URI scheme list to validate against. If not provided, the default
list is ['http', 'https', 'ftp', 'ftps']. As a reference, the IANA Web
site provides a full list of valid URI schemes.
If the valid URI schemes provided by IANA web are not what you are looking for, then I suggest you create your own field validator.
Remember that URLField is a subclass of the CharField. and since www.something.com is ok with you, then It's simple to add a regular expression to the regular CharField that checks if the pattern is correct or not.
A regular expression like this for example will validate against www and http://. so with or without http or https.
((?:https?\:\/\/|www\.)(?:[-a-z0-9]+\.)*[-a-z0-9]+.*)
www.google.com -- OK
http://www.google.com -- OK
https://www.google.com -- OK
http://google.com -- OK
https://google.com -- OK
However, this will not complain about blahwww.domain.com
so you might enhance it as you like.

Related

Django: Parameters in URLs .... which start with a slash

I use a regex for URL dispatching like described in the Django docs.
r'^remote/(?P<slug>[^/]+)/call_rfc/(?P<rfc_name>.+)$'
Unfortunately rfc_name can start with a slash!
Example:
https://example.com/remote/my_slug/call_rfc//MLK/FOO
The rfc_name is /MLK/FOO.
But this fails. Somewhere (I don't know yet if it is in the browser or in Django) the duplicate slash gets removed.
What is the best practice to handle URL parameters which can start with a slash?
It almost seems that you can consider the latest "slug" as a path. If that's the case, in your URL definition you can use path to represent that. You can have a look here to check if it helps.
path('remote/<slug:slug>/call_rfc/<path:rfc_name>', yourviewhere)
Or, you can perhaps write your custom path converter.

Django url patterns - how to get absolute url to the page?

i'm trying to get full path of the requested url in Django. I use a such url pattern:
('^', myawesomeview),
It works good for domain.com/hello, domain.com/hello/sdfsdfsd and even for domain.com/hello.php/sd""^some!bullshit.index.aspx (although, "^" is replaced with "%5E")
But when I try to use # in request (ex. http://127.0.0.1:8000/solid#url) it returns only "/sold". Is there any way to get the full path without ANY changes or replacements?
BTW, I'getting url with return HttpResponse(request.path)
Thanks in advance.
The part of URI separated by '#' sign is called a fragment identifier. Its sense is to be processed on client side only, and not to be passed to server. So if you really need this, you have to process it with JS, for example, and pass it as a usual parameter. Otherwise, this information will never be sent to Django.

Lengthening Django Username

I'm using this snippet, that allows users to only need to enter an email address to register for my app: http://djangosnippets.org/snippets/686/
Things are almost working perfectly. The problem is when a user has an email address that's above 30 characters. I get a "Ensure this value has at most 30 characters (it has 40)." error.
This is because the username is only supposed to be 30 characters. Is there a simple way to just tell Django that the username can be longer? It seems like there should be a fairly straightforward override for this.
This actually isn't simple at all. This requires subclassing the User model and using that everywhere. I've never had to do it, for this case, but it would likely cause significant issues with the Admin interface. You could also edit django's source to pull it off (ick).
Or even use this solution:
Can django's auth_user.username be varchar(75)? How could that be done?
It's quite ugly though.
You're probably better off writing an authentication backend to use the email field for authentication rather than using the username field. To populate the username (which is required) then you'd just generate some sort of random unique username maybe by hashing or using a UUID.
Hopefully this solution should help you : http://www.micahcarrick.com/django-email-authentication.html

django cross-site reverse a url

I have a similar question than django cross-site reverse. But i think I can't apply the same solution.
I'm creating an app that lets the users create their own site. After completing the signup form the user should be redirected to his site's new post form. Something along this lines:
new_post_url = 'http://%s.domain:9292/manage/new_post %site.domain'
logged_user = authenticate(username=user.username, password=user.password)
if logged_user is not None:
login(request, logged_user)
return redirect(new_product_url)
Now, I know that "new_post_url" is awful and makes babies cry so I need to reverse it in some way. I thought in using django.core.urlresolvers.reverse to solve this but that only returns urls on my domain, and not in the user's newly created site, so it doesn't works for me.
So, do you know a better/smarter way to solve this?
It looks like the domain is a subdomain of your own website so what does it matter that you can't reverse that part? Using reverse it doesn't use full domain paths, it gives you the path from the root of the project, so you can simply do something like:
new_post_uri = 'http://%s.domain:9292%s' % (site.domain, reverse('manage_new_post'))
This way you're still using reverse so you're not hardcoding the urls (and making babies cry) and you're not realy having an issue as far as I can see.
Finally, if you do not wish to hardcode your own domain in the code, uses Django's Sites model to get the current site, make sure to modify it from the default example.com to your own domain, so finally your code can be:
current_site = Site.objects.get_current() # See the docs for other options
return redirect('http://%s.%s%s' % (site.domain, current_site, reverse('manage_new_post')))
If you need to get the domain without using the Sites object, your best bet may be request.get_host(), which will get the full domain plus port, but not the protocol.
Hopefully that explains things for you. You can format things a bit better, but that's the gist of it.
redirect also optionally takes a view-name as the argument, So, since you already have all variables needed by it, just pass the view name with all the required arguments, and be done with it, rather than trying out a complicated reverse!
If you still want a reverse nature, may be you should use, get_absolute_url on the model of the Site.

How do I add a prefix to all urls and generically parse that as a kwarg

Let's say I have a site where all urls are username specific.
For example /username1/points/list is a list of that user's points.
How do I grab the /username1/ portion of the url from all urls and add it as a kwarg for all views?
Alternatively, it would be great to grab the /username1/ portion and append that to the request as request.view_user.
You might consider attacking this with middlware. Specifically using process_request. This is called before the urlresolver is called and you can do pretty much anything to the request (request.path in this case) you want to. You might strip out the username and store it in the request object. Specifics depend (obviously) on the conditions under which you do/do not want to remove the first path component.
Updated for comment:
Whichever way you go about it, when you call reverse() you have to give it the additional context info -- it can't just automagically figure it out for itself. Django doesn't play any man-behind-the-curtains games -- everything is straight Python and there isn't any global state floating around just off stage. I think this is a Good Thing™.