django- pass a string or an instance in urls.py? - django

Is there a benefit to passing a string in your url patters vs a function instance? It seems like it could be optimized to not actually load the function until it is needed, but is this in fact true?
from django.conf.urls.defaults import *
from myapp.views import myView
urlpatterns = patterns('',
# as a string
url(r'^as-string/$', "myapp.views.myView"),
# Uploading photos
url(r'^as-instance/$', myView),
)
edit: If it's true that it doesn't import until they're needed, then it would be an optimization for memory, but non-existant functions and other errors would not be raised until you actually try to visit the url.
Of course that isn't an issue if you write tests though ;)

The main benefit is that when you're working with the actual callable object you can do things like apply decorators to it in the URLConf. So you can do things like:
from django.conf.urls.defaults import *
from django.contrib.auth.decorators import login_required
from some_module import some_view
urlpatterns = patterns('',
(r'^some_url/$', some_view),
(r'^some_other_url/$', login_required(some_view)))
etc.
This lets you have decorators which are only applied to a view when you specifically want them to, rather than decorating in the views file and then having to live with it always having that decorator applied.

Perusing the source to RegexURLPattern (which is what defaults.url is using under the covers) confirms that the import only happens when and if needed, and so your ideas are correct: it's a slight optimization but does require thorough tests!

Related

Django Using reverse() with multiple URLs that include the same app

EDIT: I'm using rest_framework.reverse and providing the app name not the namespace for the reverse lookup. An example reverse call is:
reverse('api_app:system-detail', kwargs={...}, request=self.context.get('request'))
So I have a django project with a single app, and I have two different url patterns mapped to this one app. I currently have this mostly working, however for a few cases using reverse() results in the wrong namespace. My base urls.py looks like this:
from django.conf.urls import url, include
from django.views.generic.base import RedirectView
urlpatterns = [
url(r'^api/', include('api_project.api_app.urls',
namespace='api')),
url(r'^beta/api/', include('api_project.api_app.urls',
namespace='beta')),
url(r'^$', RedirectView.as_view(url='api', permanent=False),
name='index')
]
EDIT: api_app.urls looks like:
from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from . import views
from rest_framework.schemas import get_schema_view
schema_view = get_schema_view(title="Schema")
app_name = 'api_app'
urlpatterns = [
url(r'^$', views.APIRoot.as_view(), name='api-root'),
url(r'^schema/$', schema_view, name'api-schema'),
....
In most cases reverse works as expected, e.g. using /beta/endpoint has links on the page that look like:
/beta/endpoint/example
However, in a few cases using /beta/endpoint has links like:
/api/endpoint/different-example
My question is less about how reverse determines which namespace to use, but instead is this the appropriate way to use two namespaces for the same app? Is this something I should approach completely differently?
Most of my research so far has not resulted in much success, so I'm thinking this might be fundamentally the wrong approach.
If this approach is okay, then I'm curious why reverse is inconsistent in which namespace it returns.
The documentation on reversing namespaced URLs shows you what happens. In your case, you are instantiating the same app twice, once with the instance namespace api and one with the instance namespace beta.
Your application namespace is api_app, as defined by the app_name variable in your included urls.py file. So it doesn't match any of the two instance namespaces, meaning there is no default application instance.
So in your case, the URL resolver will mostly resolve to case 4: If you don't specify your current_app in the view, it will resolve to the last deployed instance (meaning the last registered), which is "beta".
You should specify your current_app in your view, both when using reverse and on the request object (so that your templates can also resolve correctly).

Django: cannot import name 'url_patterns'

I have a weird edge-case where I need to use the data stored in urls.py within a view, but since the urls.py file imports the view, I am getting a circular import error when I try to import the url data into the view.
cannot import name 'url_patterns'
Does Django have any way of grabbing all the url patterns within a view that could circumvent this error? The fact that the reverse() function exists indicates that it might.
You can solve this typically (well it depends a bit), by importing the urls in your view(s) locally. Like:
# urls.py
import someapp.views
url_patterns = [
url('some_url/', someapp.views.some_view),
]
and then import the urls in the views like:
# views.py
def some_view(request):
from someapp.urls import url_patterns
# ...
# do something with urlpatterns
# return response
pass
We here thus postpone the importing of the urls.py in the views, until the view function is actually called. By that time both the views.py and urls.py are (usually) already loaded. You can however not call the view in the views.py directly, since then the import is done before the urls.py is loaded.
Note that usually you do not have to import url_patterns, and you can use reverse(..) etc. to obtain a URL that corresponds to a view function (even passing parameters into a dictionary, etc.). So I really advice to look for Django tooling to handle URLs.
Found an answer:
from django.urls import get_resolver
url_patterns = set(v[1] for k,v in get_resolver(None).reverse_dict.items())

When using i18n_patterns, how to reverse url without language code

I am using i18n_patterns but I want to use reverse to create a link to the page without language in the url (such that the user will be redirected based on cookies and headers and such).
I have tried
from django.utils.translation import activate, deactivate, get_language
current_lang = get_language()
deactivate()
url = reverse(things)
activate(current_lang)
That works for getting other language versions using activate(target_lang), but if I deactivate I just get urls for the default language (/en/account/ but I want /account/).
I already thought getting alternate language versions is overly complicated, but this I cannot manage at all. Any hints? (Without manually stripping LANGUAGE_CODE from the url)
UPDATE: I also tried
from django.core.urlresolvers import get_resolver
get_resolver(None).reverse(*args, **kwargs)
but get NoReverseMatch
I think the easiest way is to let Django resolve the URL with the language prefix and then just remove the language prefix.
You can write the following function:
import re
from django.core.urlresolvers import reverse
def reverse_no_i18n(viewname, *args, **kwargs):
result = reverse(viewname, *args, **kwargs)
m = re.match(r'(/[^/]*)(/.*$)', result)
return m.groups()[1]
Now, anywhere in your code you can do something like this:
from myproject.utils import reverse_no_i18n
def my_view(request):
return HttpResponseRedirect(reverse_no_i18n('my_view_name'))
You might also want to create a custom {% url %} templatetag which calls your custom function.
I also spent time to find a nice solution and here is mine.
Next to main urls file ('my_project/urls.py'), create the file 'my_project/urls_without_lang.py' with the content below.
Then, you can use reverse('viewname', urlconf='my_project.urls_without_lang')
Django=<1.11
from copy import copy
from django.urls.resolvers import LocaleRegexURLResolver
from .urls import urlpatterns as urlpatterns_i18n
"""
Purpose of this file is to be able to reverse URL patterns without language prefix.
This is usefull to build URL meant to be communicated "outside" of the domain without any language duty.
To use it with 'reverse' method (from django.shortcuts module), simply give the additional parameter:
`urlconf='my_project.urls_without_lang'`
Example: `reverse('viewname', urlconf='my_project.urls_without_lang')`
"""
urlpatterns = copy(urlpatterns_i18n)
for el in urlpatterns_i18n:
if isinstance(el, LocaleRegexURLResolver):
urlpatterns.remove(el)
urlpatterns += el.url_patterns
Django>1.11
from copy import copy
from django.urls import URLResolver
from .urls import urlpatterns as urlpatterns_i18n
urlpatterns = copy(urlpatterns_i18n)
for el in urlpatterns_i18n:
if isinstance(el, URLResolver) and isinstance(el.urlconf_name, list):
urlpatterns.remove(el)
urlpatterns += el.url_patterns
Hope that will help some of you.

Which to use in Django: ListView or list_detail?

I was reading some tutorials and books about generic views.
In part 4 of the official tutorial, they wrote an example like this
from django.conf.urls import patterns, include, url
from django.views.generic import DetailView, ListView
from polls.models import Poll
urlpatterns = patterns('',
url(r'^$',
ListView.as_view(
queryset=Poll.objects.order_by('-pub_date')[:5],
context_object_name='latest_poll_list',
template_name='polls/index.html')),
url(r'^(?P<pk>\d+)/$',
DetailView.as_view(
model=Poll,
template_name='polls/detail.html')),
url(r'^(?P<pk>\d+)/results/$',
DetailView.as_view(
model=Poll,
template_name='polls/results.html'),
name='poll_results'),
url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
)
I have also been reading The Definitive Guide to Django: Web Development Done Right, Second Edition and when they talked about generic views, they wrote their example like this
from django.conf.urls.defaults import *
from django.views.generic import list_detail
from mysite.books.models import Publisher
publisher_info = {
'queryset': Publisher.objects.all(),
'template_name': 'publisher_list_page.html',
}
urlpatterns = patterns('',
(r'^publishers/$', list_detail.object_list, publisher_info)
)
Should I be using ListView or list_detail? They both come from django.views.generic. If they can both be used, then what's the difference (advantage and disadvantage comparison)?
In case it helps, I'll explain my objective: In my project, I want to list work orders, and then I want a detailed view of each work order that will also contain a list of comments for that order (similar to comments per blog post).
I'm finding Classy useful as an easy way to view an outline of each CBV: http://ccbv.co.uk/projects/Django/1.6/django.views.generic.list/ListView/
It feels like a missing part of the Django docs now.
ListView (class-based) is intended to replace object_list (function-based) because there is limited flexibility to extending a function's behavior.
As the Django (1.4) documentation notes, the function-based generic views are deprecated in favor of the class-based versions. So use ListView, since Django removed object_list.
Either way, I prefer to put all the customizations in views.py to avoid cluttering up urls.py, which tends to be a dumping ground of things.

Django import views multiple times in urls.py

In Django, is it not possible to have multiple views imports from the urls.py?
For example, I have the following code in urls.py:
from mysite.books import views
from mysite.contact import views
urlpatterns = patterns('',
(r'^contact/$', views.contact),
(r'^search/$', views.search),
)
However, the server displays an error unless I disable one of the couples. So my questions are threefold:
1) Is it not possible to have multiple import views statements?
2) How to get around this?
3) What is best practice for where to put all your views.py? One file? Multiple files? etc.
Thank you.
1) Yes it is.
2)
from mysite.books import views as books_views
from mysite.contact import views as contact_views
urlpatterns = patterns('',
(r'^contact/$', contact_views.contact),
(r'^search/$', books_views.search),
)
3) Per Django docs, "This code can live anywhere you want, as long as it’s on your Python path.". I keep all the app views in app/views.py
You can import as many things as you like, but objects have to have unique names for them to be distinguished.
There are a couple of ways of dealing with this. One is to simply import the functions, rather than the module:
from mysite.books.views import books
from mysite.contact.views import contact
This is obviously only good if you only have one or two views in each file. A second option is to import the modules under different names:
from mysite.books import views as books_views
from mysite.contact import views as contact_views
A third option is not to import the views at all, but use strings to refer to them:
urlpatterns = patterns('',
(r'^contact/$', 'contact.views.contact'),
(r'^search/$', 'book.views.search'),
)
A fourth is to have separate urls.py for each application, and include the urlconfs in the main urls.py.
I think that another option would be:
urlpatterns = patterns('mysite.books.views',
(r'^contact/$, 'contact'),
)
urlpatterns += patterns('mysite.contact.views',
(r'^search/$, 'search'),
)
as described in djangobook.