How to unit test urls.py when included by project? - python

How can I run unit tests to validate that a certain URL calls a particular function?
I want to do something like this:
class HomePageTest(TestCase):
def test_root_url_resolves_to_list_view(self):
found = resolve('/testme/')
self.assertEqual(found.func.func_name, ListView.__name__)
#self.assertEqual(found.func, ListView.as_view())
But lets imagine the apps urls.py is included in the projects urls.py under something like:
url(r'^submodule/$', include('fhqna.urls')),
How can I write the test included in the app so it checks the url "/testme/" indepent of how it is included? ("/submodule/testme/" in this example)?

You can configure urls for test case
class HomePageTest(TestCase):
urls = 'fhqna.urls'
def test_root_url_resolves_to_list_view(self):
found = resolve('/testme/')
self.assertEqual(found.func.func_name, ListView.__name__)
Or give a name to your url and resolve it by this name regardless of actual url is used. In this case you don't need to configure urls for TestCase.

Related

Simple django unit tests giving 404 - What is the correct setup procedure?

I'm a ways down my Django project and have decided unit tests are a good idea. I am trying to write a simple unit test around a view, but I'm getting <h1>Not Found</h1><p>The requested URL /index was not found on this server.</p> from the response. Why is that happening?
Here is my unit test..
from django.test import TestCase
class BookerIndexTests(TestCase):
def test_anonymous_request(self):
response = self.client.get('booker:index')
self.assertEqual(response.status_code, 200)
Within my urls.py, I have this line for my index: url(r'^$', views.index, name='index'),
Am I missing a setup step here? Why is this basic unit test throwing a 404 error?
As Daniel Roseman pointed out, you can't use a pattern name directly in client.get(). If you want to use the pattern name rather than the paths themselves, you can use reverse. You code could look like this:
from django.test import TestCase
from django.core.urlresolvers import reverse
class BookerIndexTests(TestCase):
def test_anonymous_request(self):
response = self.client.get(reverse('booker:index'))
self.assertEqual(response.status_code, 200)
This is typically what I do in my test suites because I prefer to use the pattern names over the paths.
You're passing a URL pattern name to client.get(), rather than an actual path. You need to pass the actual index path, which is - according to that urlconf - "/".

How to get a url map in django? [duplicate]

Is there a way to get the complete django url configuration?
For example Django's debugging 404 page does not show included url configs, so this is not the complete configuration.
Django extensions provides a utility to do this as a manage.py command.
pip install django-extensions
Then add django_extensions to your INSTALLED_APPS in settings.py. then from the console just type the following
python manage.py show_urls
Django is Python, so introspection is your friend.
In the shell, import urls. By looping through urls.urlpatterns, and drilling down through as many layers of included url configurations as possible, you can build the complete url configuration.
import urls
urls.urlpatterns
The list urls.urlpatterns contains RegexURLPattern and RegexURLResolver objects.
For a RegexURLPattern object p you can display the regular expression with
p.regex.pattern
For a RegexURLResolver object q, which represents an included url configuration, you can display the first part of the regular expression with
q.regex.pattern
Then use
q.url_patterns
which will return a further list of RegexURLResolver and RegexURLPattern objects.
At the risk of adding a "me too" answer, I am posting a modified version of the above submitted script that gives you a view listing all the URLs in the project, somewhat prettified and sorted alphabetically, and the views that they call. More of a developer tool than a production page.
def all_urls_view(request):
from your_site.urls import urlpatterns #this import should be inside the function to avoid an import loop
nice_urls = get_urls(urlpatterns) #build the list of urls recursively and then sort it alphabetically
return render(request, "yourapp/links.html", {"links":nice_urls})
def get_urls(raw_urls, nice_urls=[], urlbase=''):
'''Recursively builds a list of all the urls in the current project and the name of their associated view'''
from operator import itemgetter
for entry in raw_urls:
fullurl = (urlbase + entry.regex.pattern).replace('^','')
if entry.callback: #if it points to a view
viewname = entry.callback.func_name
nice_urls.append({"pattern": fullurl,
"location": viewname})
else: #if it points to another urlconf, recur!
get_urls(entry.url_patterns, nice_urls, fullurl)
nice_urls = sorted(nice_urls, key=itemgetter('pattern')) #sort alphabetically
return nice_urls
and the template:
<ul>
{% for link in links %}
<li>
{{link.pattern}} ----- {{link.location}}
</li>
{% endfor%}
</ul>
If you wanted to get real fancy you could render the list with input boxes for any of the regexes that take variables to pass to the view (again as a developer tool rather than production page).
This question is a bit old, but I ran into the same problem and I thought I would discuss my solution. A given Django project obviously needs a means of knowing about all its URLs and needs to be able to do a couple things:
map from a url -> view
map from a named url -> url (then 1 is used to get the view)
map from a view name -> url (then 1 is used to get the view)
Django accomplishes this mostly through an object called a RegexURLResolver.
RegexURLResolver.resolve (map from a url -> view)
RegexURLResolver.reverse
You can get your hands on one of these objects the following way:
from my_proj import urls
from django.core.urlresolvers import get_resolver
resolver = get_resolver(urls)
Then, you can simply print out your urls the following way:
for view, regexes in resolver.reverse_dict.iteritems():
print "%s: %s" % (view, regexes)
That said, Alasdair's solution is perfectly fine and has some advantages, as it prints out some what more nicely than this method. But knowing about and getting your hands on a RegexURLResolver object is something nice to know about, especially if you are interested in Django internals.
The easiest way to get a complete list of registered URLs is to install contrib.admindocs then check the "Views" section. Very easy to set up, and also gives you fully browsable docs on all of your template tags, models, etc.
I have submitted a package (django-showurls) that adds this functionality to any Django project, it's a simple new management command that integrates well with manage.py:
$ python manage.py showurls
^admin/
^$
^login/$
^logout/$
.. etc ..
You can install it through pip:
pip install django-showurls
And then add it to your installed apps in your Django project settings.py file:
INSTALLED_APPS = [
..
'django_showurls',
..
]
And you're ready to go.
More info here -
https://github.com/Niklas9/django-showurls
If you want a list of all the urls in your project, first you need to install django-extensions
You can simply install using command.
pip install django-extensions
For more information related to package goto django-extensions
After that, add django_extensions in INSTALLED_APPS in your settings.py file like this:
INSTALLED_APPS = (
...
'django_extensions',
...
)
urls.py example:
from django.urls import path, include
from . import views
from . import health_views
urlpatterns = [
path('get_url_info', views.get_url_func),
path('health', health_views.service_health_check),
path('service-session/status', views.service_session_status)
]
And then, run any of the command in your terminal
python manage.py show_urls
or
./manage.py show_urls
Sample output example based on config urls.py:
/get_url_info django_app.views.get_url_func
/health django_app.health_views.service_health_check
/service-session/status django_app.views.service_session_status
For more information you can check the documentation.
Are you looking for the urls evaluated or not evaluated as shown in the DEBUG mode? For evaluated, django.contrib.sitemaps can help you there, otherwise it might involve some reverse engineering with Django's code.
When I tried the other answers here, I got this error:
django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
It looks like the problem comes from using django.contrib.admin.autodiscover() in my urls.py, so I can either comment that out, or load Django properly before dumping the URL's. Of course if I want to see the admin URL's in the mapping, I can't comment them out.
The way I found was to create a custom management command that dumps the urls.
# install this file in mysite/myapp/management/commands/urldump.py
from django.core.management.base import BaseCommand
from kive import urls
class Command(BaseCommand):
help = "Dumps all URL's."
def handle(self, *args, **options):
self.show_urls(urls.urlpatterns)
def show_urls(self, urllist, depth=0):
for entry in urllist:
print ' '.join((" " * depth, entry.regex.pattern,
entry.callback and entry.callback.__module__ or '',
entry.callback and entry.callback.func_name or ''))
if hasattr(entry, 'url_patterns'):
self.show_urls(entry.url_patterns, depth + 1)
If you are running Django in debug mode (have DEBUG = True in your settings) and then type a non-existent URL you will get an error page listing the complete URL configuration.

Flask Custom Routing

Say that I'm defining a user profile page in Flask:
#app.route('/user/<name>')
def user(name):
do stuff
I'd like to change the routing rules so that I can put more than just one designation in the <name>, e.g. <name, location> to translate to the user with that name and location, url given by /user/James-Oregon.
If I understand you correctly, you're looking for...
#app.route('/user/<name>-<location>')
def user(name, location):
# do stuff...
When using URL /user/James-Oregon, you should get "James" for name and "Oregon" for location.
Note that flask is largely built off of Werkzeug, so be sure to check out the Werkzeug routing documentation as well.

Shared/Global Dictionary in Django Between URLs and Context Processor

At runtime I'm trying to generate a tree of parent-child relationships between views using the urls.py of different apps. I'm trying to accomplish breadcrumbs by allowing this tree to be defined by an extension of the url function that accepts extra arguments for view_name (name to display on page when used on page, like "Home") and parent_view (specifies the immediate parent so you can generate your breadcrumb).
This class is defined in a separate file in its own module utils.breadcrumbs. The class is called BreadCrumbs and I try to define an instance of BreadCrumbs in the same file for import into various files. This is where it breaks I think.
utils/breadcrumbs.py
class BreadCrumbs:
breadcrumbs = {} # This is our tree
def url(self, pattern, view, arguments={}, name=None, view_name=None, parent_view=None):
... Adds node to self.breadcrumbs ...
return url(pattern, view, arguments, name)
bc = BreadCrumbs()
app/urls.py
from utils.breadcrumbs import bc
urlpatterns = patterns('',
bc.url(r'^home/$', 'app.views.home', name='home', view_name='Home'),
bc.url(r'^subpage/$', 'app.views.subpage', view_name='Sub Page', parent_view="app.views.home"),
)
Then I try to access the tree defined in breadcrumbs.bc in a context processor using the view name given through a middleware. When I had all of my url patterns in the core urls.py file instead of in separate apps, it worked fine. Now that I've moved the url patterns to separate files, the tree is empty when I go to call it in my context processor using a from utils.breadcrumbs import bc. Am I using global variables incorrectly here? Is there a more correct method to share a variable between my urls.py and my context processor? I've looked at sessions, but I don't have access to the request in urls.py, correct?
Your help is appreciated in advance.

Defining Constants in Django

I want to have some constants in a Django Projects. For example, let's say a constant called MIN_TIME_TEST.
I would like to be able to access this constant in two places: from within my Python code, and from within any Templates.
What's the best way to go about doing this?
EDIT:
To clarify, I know about Template Context Processors and about just putting things in settings.py or some other file and just importing.
My question is, how do I combine the two approaches without violating the "Don't Repeat Yourself" rule? Based on the answers so far, here's my approach:
I'd like to create a file called global_constants.py, which will have a list of constants (things like MIN_TIME_TEST = 5). I can import this file into any module to get the constants.
But now, I want to create the context processor which returns all of these constants. How can I go about doing this automatically, without having to list them again in a dictionary, like in John Mee's answer?
Both Luper and Vladimir are correct imho but you'll need both in order to complete your requirements.
Although, the constants don't need to be in the settings.py, you could put them anywhere and import them from that place into your view/model/module code. I sometimes put them into the __init__.py if I don't care to have them to be considered globally relevant.
a context processor like this will ensure that selected variables are globally in the template scope
def settings(request):
"""
Put selected settings variables into the default template context
"""
from django.conf import settings
return {
'DOMAIN': settings.DOMAIN,
'GOOGLEMAPS_API_KEY': settings.GOOGLEMAPS_API_KEY,
}
But this might be overkill if you're new to django; perhaps you're just asking how to put variables into the template scope...?
from django.conf import settings
...
# do stuff with settings.MIN_TIME_TEST as you wish
render_to_response("the_template.html", {
"MIN_TIME_TEST": settings.MIN_TIME_TEST
}, context_instance=RequestContext(request)
To build on other people's answers, here's a simple way you'd implement this:
In your settings file:
GLOBAL_SETTINGS = {
'MIN_TIME_TEST': 'blah',
'RANDOM_GLOBAL_VAR': 'blah',
}
Then, building off of John Mee's context processor:
def settings(request):
"""
Put selected settings variables into the default template context
"""
from django.conf import settings
return settings.GLOBAL_SETTINGS
This will resolve the DRY issue.
Or, if you only plan to use the global settings occasionally and want to call them from within the view:
def view_func(request):
from django.conf import settings
# function code here
ctx = {} #context variables here
ctx.update(settings.GLOBAL_SETTINGS)
# whatever output you want here
Consider putting it into settings.py of your application. Of course, in order to use it in template you will need to make it available to template as any other usual variable.
Use context processors to have your constants available in all templates (settings.py is a nice place to define them as Vladimir said).
Context processors are better suited at handling more dynamic object data--they're defined as a mapping in the documentation and in many of the posts here they're being modified or passed around to views--it doesn't make sense that a template may lose access to global information because, for example, your forgot to use a specialized context processor in the view. The data is global by definition & that couples the view to the template.
A better way is to define a custom template tag. This way:
templates aren't relying on views to have global information passed into them
it's DRY-er: the app defining the global settings can be exported to many projects, eliminating common code across projects
templates decide whether they have access to the global information, not the view functions
In the example below I deal with your problem--loading in this MIN_TIME_TEST variable--and a problem I commonly face, loading in URLs that change when my environment changes.
I have 4 environments--2 dev and 2 production:
Dev: django-web server, url: localhost:8000
Dev: apache web server: url: sandbox.com -> resolves to 127.0.0.1
Prod sandbox server, url: sandbox.domain.com
Prod server: url: domain.com
I do this on all my projects & keep all the urls in a file, global_settings.py so it's accessible from code. I define a custom template tag {% site_url %} that can be (optionally) loaded into any template
I create an app called global_settings, and make sure it's included in my settings.INSTALLED_APPS tuple.
Django compiles templated text into nodes with a render() method that tells how the data should be displayed--I created an object that renders data by returnning values in my global_settings.py based on the name passed in.
It looks like this:
from django import template
import global_settings
class GlobalSettingNode(template.Node):
def __init__(self, settingname):
self.settingname = settingname;
def render(self, context):
if hasattr(global_settings, self.settingname):
return getattr(global_settings, self.settingname)
else:
raise template.TemplateSyntaxError('%s tag does not exist' % self.settingname)
Now, in global_settings.py I register a couple tags: site_url for my example and min_test_time for your example. This way, when {% min_time_test %} is invoked from a template, it'll call get_min_time_test which resolves to load in the value=5. In my example, {% site_url %} will do a name-based lookup so that I can keep all 4 URLs defined at once and choose which environment I'm using. This is more flexible for me than just using Django's built in settings.Debug=True/False flag.
from django import template
from templatenodes import GlobalSettingNode
register = template.Library()
MIN_TIME_TEST = 5
DEV_DJANGO_SITE_URL = 'http://localhost:8000/'
DEV_APACHE_SITE_URL = 'http://sandbox.com/'
PROD_SANDBOX_URL = 'http://sandbox.domain.com/'
PROD_URL = 'http://domain.com/'
CURRENT_ENVIRONMENT = 'DEV_DJANGO_SITE_URL'
def get_site_url(parser, token):
return GlobalSettingNode(CURRENT_ENVIRONMENT)
def get_min_time_test(parser, token):
return GlobalSettingNode('MIN_TIME_TEST')
register.tag('site_url', get_site_url)
register.tag('min_time_test', get_min_time_test)
Note that for this to work, django is expecting global_settings.py to be located in a python packaged called templatetags under your Django app. My Django app here is called global_settings, so my directory structure looks like:
/project-name/global_settings/templatetags/global_settings.py
etc.
Finally the template chooses whether to load in global settings or not, which is beneficial for performance. Add this line to your template to expose all the tags registered in global_settings.py:
{% load global_settings %}
Now, other projects that need MIN_TIME_TEST or these environments exposed can simply install this app =)
In the context processor you can use something like:
import settings
context = {}
for item in dir(settings):
#use some way to exclude __doc__, __name__, etc..
if item[0:2] != '__':
context[item] = getattr(settings, item)
Variant on John Mee's last part, with a little elaboration on the same idea Jordan Reiter discusses.
Suppose you have something in your settings akin to what Jordan suggested -- in other words, something like:
GLOBAL_SETTINGS = {
'SOME_CONST': 'thingy',
'SOME_OTHER_CONST': 'other_thingy',
}
Suppose further you already have a dictionary with some of the variables you'd like to pass your template, perhaps passed as arguments to your view. Let's call it my_dict. Suppose you want the values in my_dict to override those in the settings.GLOBAL_SETTINGS dictionary.
You might do something in your view like:
def my_view(request, *args, **kwargs)
from django.conf import settings
my_dict = some_kind_of_arg_parsing(*args,**kwargs)
tmp = settings.GLOBAL_SETTINGS.copy()
tmp.update(my_dict)
my_dict = tmp
render_to_response('the_template.html', my_dict, context_instance=RequestContext(request))
This lets you have the settings determined globally, available to your templates, and doesn't require you to manually type out each of them.
If you don't have any additional variables to pass the template, nor any need to override, you can just do:
render_to_response('the_template.html', settings.GLOBAL_SETTINGS, context_instance=RequestContext(request))
The main difference between what I'm discussing here & what Jordan has, is that for his, settings.GLOBAL_SETTINGS overrides anything it may have in common w/ your context dictionary, and with mine, my context dictionary overrides settings.GLOBAL_SETTINGS. YMMV.

Categories