Django and testing local URLs exist - django

I have a model which needs to store URLs which will be part of the Django environment. If I was storing normal URLs, I'd use models.URLField, and use verify_exists to make sure the URL actually exists.
However, this doesn't work that great in development, since the dev server is single-threaded, it hangs indefinitely, since it can't process two requests at once.
I was hoping to do something using resolve(), but am having difficulty adapting the function myview at the end of that documentation page to a version which does not take a request, since I want to check that a given local URL can be resolved, and called without a 404 being raised, from a blank session.
I was hoping to do this with a validator, something like this:
def validate_local_url(value):
try:
view, args, kwargs = resolve(value)
view(*args, **kwargs)
except Resolver404:
raise ValidationError(u'%s is not a local URL (not a valid URL)' % value)
except Http404:
raise ValidationError(u'%s is not a local URL (does not exist)' % value)
However, this fails without a valid request object being passed into kwargs. How do I generate a dummy (blank) request object? I've tried just using django.http.HttpRequest.

Just a wild idea, not sure if it will be helpful. Have you considered naming the urls and using reverse()? Reverse will work if the URL is valid and will fail when it is not.

Are you cool with using the django test Client?
If so, this should do it:
from django.test.client import Client
def validate_local_url(path):
c = Client()
try:
resp = c.get(path)
if resp.status_code == 404:
raise ValidationError(u'%s is not a local URL (does not exist)' % value)
except:
raise ValidationError(u'%s is not a local URL (not a valid URL)' % value)
Just, you know, make sure under penalty of death that validate_local_url can never be called by a local GET request, otherwise someone can trivially set your server on an infinite loop:
# urls.py
url('^infinite_loop/$', 'myapp.infinite_loop', 'infinite_loop')
#views.py
def infinite_loop_view(request, template_name="blah.html", form_class=MyForm):
my_form = form_class(request.REQUEST or None) # yes, admittedly this is dumb
if my_form.is_valid():
return HttpResponse("Congratulations! Your path was totally valid.")
return render_to_response(template_name, locals(), RequestContext(request))
And then:
http://example.com/infinite_loop/?path_field=infinite_loop

Related

Alternative of Requests in django

In my project I'm trying to hit a url(which is running in same project) in my view.
so in simplest way I can explain here.
#login_required
def my_api_view(request):
if requests.method == 'GET':
# do stuff
return JsonResponse()
# and its url is `/api/get-info/`
another view which is consuming above api
#login_required
def show_info(request):
url = get_base_url + /api/get-info/ # http://localhost:8000/api/get-info/
r = requests.get(url)
return HttpResponse(r.json())
Now I have to use same session (login required) so when I hit the url using requests it complains that user is not loggedin which is obviously correct.
How can I do this elegantly and efficienty. session use of logged in user. (I dont want to call the view as a function, I want to hit the api-url end point and consume.
PS: we have something similar in django Test self.client.get(...)
Just call that view function and pass the request object as parameter to it.
#login_required
def show_info(request):
r = my_api_view(request)
return HttpResponse(r.json())
Or a better option would be to simply separate the logic into a separate function, as mentioned by #koniiiik in the comments.
EDIT: Or if you really want to hit the URL endpoint, you can simply pass on the cookie values to the request you make.
#login_required
def show_info(request):
url = get_base_url + "/api/get-info/" # http://localhost:8000/api/get-info/
r = requests.get(url, cookies=request.COOKIES)
return HttpResponse(r.json())

Show error message when decorator fails

The decorator is working fine but I would like to display an error message (I'd like to use messages framework) if the user doesn't belong to any of the required groups. Here's the decorator:
def group_required(*group_names):
"""Requires user membership in at least one of the groups passed in."""
def in_groups(user):
if user.is_authenticated():
if bool(user.groups.filter(name__in=group_names)) or user.is_superuser:
return True
return False
return user_passes_test(in_groups)
I call it using something like:
#require_http_methods(['GET'])
#group_required('supervisor')
def home_view(request):
return render(request, 'home.html')
I tried using this snippet to use messages framework (since this requires the request object) but it realized that messages framework middleware didn't appear installed inside the decorator.
I'm willing to change whatever it takes :)
Update:
What I'm looking for:
def group_required(request, *group_names):
"""Requires user membership in at least one of the groups passed in."""
def in_groups(user):
if user.is_authenticated():
if user.groups.filter(name__in=group_names).exists() or user.is_superuser:
return True
else:
# I'm getting:
# You cannot add messages without installing django.contrib.messages.middleware.MessageMiddleware
messages.add_message(request, messages.ERROR, 'Group is not allowed')
return False
return user_passes_test(in_groups, request)
I don't think you really need threadlocals in this use case. And normally when threadlocals seems to be the only way to go in a Django app, there could be some mis-structured context layers. Comparing w/ the threadlocals, I would rather to duplicate user_passes_test and then modify it to pass request to in_groups (We could not pass request to is_group easily without modifying the code of user_passes_test. Check the question: How to pass Django request object in user_passes_test decorator callable function.) (Maybe a ticket for this?)
Furthermore, bool(user.groups.filter(name__in=group_names)) would caused items to be retrieved to DB adapter and Python instance before deciding the existence, using exists() and thus user.groups.filter(name__in=group_names).exists() to directly return bool result from DB backend is far more efficient here.

django test client gets 404 for all urls

I am doing my first experiments with django testing and I am having the problem that I always get the 404 template regardless which url (even /) I am using.
If I throw the very same code into the django shell it's working as expected and always presents me the contents of the requested url.
class SimpleTest(TestCase):
def setUp(self):
self.user = User.objects.create_user('test', 'test', 'test')
self.user.is_staff = True
self.user.save()
self.client = Client()
def test_something(self):
self.assertTrue(self.client.login(username='test', password= 'test'))
self.client.get("/")
The login returns True, but the get() fails. Any hints what I am doing wrong here?
Keep in mind that most views use something like get_object_or_404, get_list_or_404, or simply raise Http404 when there's a problem accessing some object or another. You'll need to make sure that your test database is populated with sufficient objects to fulfill all these requirements to make the view not return a 404.
Remember, when running tests, the database is rolled back after each test (using transactions), so each test method must stand on its own or the setUp method must populate the database with any required dependencies.

Django process_view middleware resulting in 403 forbidden

I've written a small bit of middleware that catches if a user is using a temporary password and, if so, redirects them to a page that forces them to create a new password. My problem is that the page works fine when the user is logged in and NOT using a temp password (i.e. they go to the change password URL manually), but when they ARE using a temp password the redirect from the middleware yields a 403 Forbidden page.
The middleware does one other thing in process_view after the temp password check, but this is the relevant code:
class MyMiddleware( object ):
def process_view( self, request, view_func, view_args, view_kwargs ):
if request.user.is_authenticated( ):
try:
if request.user.get_profile( ).using_temp:
return HttpResponseRedirect( reverse( 'change_password' ) )
except Object.DoesNotExist:
pass
# Not using temp password, let the request process
return None
Note that rendering the template directly could be used, with something like render_to_response, to fix the problem but that will cause the browser's URL to not follow as well as it not being able to really exit the page it renders.
First, I think your indenting is off in the example, but how about the following as a solution to detect when the current path is the change_password URL? This should get rid of that infinite redirect you have going on.
class MyMiddleware( object ):
def process_view( self, request, view_func, view_args, view_kwargs ):
if request.user.is_authenticated( ):
try:
if request.user.get_profile( ).using_temp and request.path != reverse('change_password'):
return HttpResponseRedirect( reverse( 'change_password' ) )
except Object.DoesNotExist:
pass
# Not using temp password, let the request process
return None
Which version of django are you using ?
If your are using the latest beta, setting the logging may be helpful
http://docs.djangoproject.com/en/dev/topics/logging/
Django Debug Toolbar might be helpful here. It can trap redirects and show you where it's redirecting before actually going there. This helps run down broken redirects.
That said, I'd recommend using a different "change password" page for users with temporary passwords, so it can handle permissions checking differently. The page you have might have a #login_required decorator, and a temporary password might not be considered "really" logged in.

redirect to another page instead of recieving Django standarf 404 error

i have something like this in my views.py
instance = get_object_or_404(register,pk=request.user.id)
Now if there is no related object against this user i receive i standard django 404 eror
saying no matches found.
what i want here is instead of receiving this 404 error redirect it to another page say "something.html". but i dont know how. i am using method = "POST"
is there any way to redirect it to other page instead of receiving a 404 error
using a try/except block you can redirect if the object is not found
try:
instance = register.get(pk=request.user.id)
except register.DoesNotExist:
return HttpResponseRedirect('url that renders something.html')
FYI, definition of django get_object_or_404 function looks like this
def get_object_or_404(klass, *args, **kwargs):
"""
Uses get() to return an object, or raises a Http404 exception if the object
does not exist.
klass may be a Model, Manager, or QuerySet object. All other passed
arguments and keyword arguments are used in the get() query.
Note: Like with get(), an MultipleObjectsReturned will be raised if more than one
object is found.
"""
queryset = _get_queryset(klass)
try:
return queryset.get(*args, **kwargs)
except queryset.model.DoesNotExist:
raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
from the docs, If you raise Http404 at any point in a view function, Django will catch it and return the standard error page for your application, along with an HTTP error code 404.
look at customizing error views if you want to render a custom 404.html based on the context variables
Depending on what the Big Picture is, you might want to look at django.contrib.flatpages and see what they are doing. Basically they are dealing with the 404 in middleware and then looking at the path to decided if there is something they can return. I have used variations on this on a couple of sites.