I have a pretty standard django app, and am wondering how to set the url routing so that I don't have to explicitly map each url to a view.
For example, let's say that I have the following views: Project, Links, Profile, Contact. I'd rather not have my urlpatterns look like this:
(r'^Project/$', 'mysite.app.views.project'),
(r'^Links/$', 'mysite.app.views.links'),
(r'^Profile/$', 'mysite.app.views.profile'),
(r'^Contact/$', 'mysite.app.views.contact'),
And so on. In Pylons, it would be as simple as:
map.connect(':controller/:action/:id')
And it would automatically grab the right controller and function. Is there something similar in Django?
mods = ('Project','Links','Profile','Contact')
urlpatterns = patterns('',
*(('^%s/$'%n, 'mysite.app.views.%s'%n.lower()) for n in mods)
)
Unless you have a really huge number of views, writing them down explicitly is not too bad, from a style perspective.
You can shorten your example, though, by using the prefix argument of the patterns function:
urlpatterns = patterns('mysite.app.views',
(r'^Project/$', 'project'),
(r'^Links/$', 'links'),
(r'^Profile/$', 'profile'),
(r'^Contact/$', 'contact'),
)
You might be able to use a special view function along these lines:
def router(request, function, module):
m =__import__(module, globals(), locals(), [function.lower()])
try:
return m.__dict__[function.lower()](request)
except KeyError:
raise Http404()
and then a urlconf like this:
(r'^(?P<function>.+)/$', router, {"module": 'mysite.app.views'}),
This code is untested but the general idea should work, even though you should remember:
Explicit is better than implicit.
Related
Let's say I have LOGIN_REDIRECT_URL='/foo/bar/' and I am on http://127.0.0.1/en/login/ and I login successfully. I will be redirected to http://127.0.0.1/foo/bar/ resulting in losing the language prefix. What should I do in order to maintain the language code in the url? Of course, I want all the other languages codes work and also the default one just like that:
http://127.0.0.1/en/login/ -> http://127.0.0.1/en/foo/bar/
http://127.0.0.1/login/ -> http://127.0.0.1/foo/bar/
http://127.0.0.1/ro/login/ -> http://127.0.0.1/ro/foo/bar/
In that case you can extend Django's LoginView and override the get_success_url method.
Something like this:
from django.contrib.auth.views import LoginView
from django.utils.translation import get_language
class CustomLoginView(LoginView):
def get_success_url(self):
# lang = get your language from the URL (it'd be helpful if you post your URLs in order to help better) or you can use like this:
lang = get_language()
url = '{}/{}/login'.format(yourbaseurl, lang)
return url
Assuming you are using Django auth and the LoginView from the framework, you can parametrize the URL to redirect to after logging in. The LOGIN_REDIRECT_URL is a fallback when it is not specified.
The LoginView attribute is called next. The next url can be set using a parameter from the previous page like ?next=<your_url>. Just don't forget to sanitize it using is_safe_url() from django.utils.http.
I was working on an application wherein I created a generic ListView. Now, while defining that view in my urls.py, I read from the documentation that I need to use the as_view() method as follows:
from django.conf.urls import patterns, include, url
from .views import BlogIndex
urlpatterns = patterns(
'',
url(r'^$', BlogIndex.as_view(), name="index"),
)
Now, I didn't really understood what the documentation had to say about this method. Can someone shed some light into this concept?
In Class-based views, you have to call as_view() function so as to return a callable view that takes a request and returns a response. Its the main entry-point in request-response cycle in case of generic views.
as_view is the function(class method) which will connect my MyView class with its url.
From django docs:
classmethod as_view(**initkwargs)
Returns a callable view that takes a request and returns a response:
You just can't use class-based views like you could in normal function-based views.
BlogIndex(request) # can't do this in case of CBVs
The above code is not valid if you want the CBVs to function properly. For that, you need to provide a view which is callable and then pass request to it. For example:
response = MyView.as_view()(request) # valid way
By calling the as_view() function on my view class MyView will give me a view which i will call with request parameter to initiate the request-response cycle.
In your case:
my_callable_view = BlogIndex.as_view() # returns a callable view
<function blog.views.BlogIndex>
Now, call this function and pass the request.
response = my_callable_view(request) # generate proper response
view function have different format than before because :
This view will actually be implemented as a class
We will be inheriting from an existing generic view function that already does most of what we want this view function to do, rather
than writing our own from scratch.
Class method as_view()- this does all the work of creating an instance of the class, and making sure that the right handler methods
are called for incoming HTTP requests.
ref : https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Generic_views
Maybe I can try to write the as_view with pseudo-code:
class ViewClass():
#other stuff
def as_view(self):
return self.render(*args)
It return the render function inside the CBV.
So it actually is the same as path('some_path',views.some_view_function,name='some_name')
Of course, there there is actually much more things going on than only render function, for example to verify and save the content inside post queryDict, actually you need def post(): to handle the post, in function you just if request.method == 'POST' they are actually mutual.
To be specific, as_view() just generate a overall function, including if request.method =='POST': #some code Maybe the actual code doesn't work like that, but you could understand it this way if you are not prepared to contribute to django source code yourself.
can i substitute the 2 url routes
urlpatterns = patterns('',
url(r'service/geticons', 'core.service.geticons'),
url(r'service/getnearby', 'core.service.getnearby'),
by a single, more generic, route that routes all requests to the function in the service module with the name of the last url segment?
thinking about something like
url(r'service/#f', 'core.service.#f')
or must i do such dispatch in the service module in django?
Sure, you could collect the path and point it to a view that returns the function.
url(r'service/(?P<function>\w+)/$', 'core.service.function_router')
def function_router(request, function):
return globals()[function](request)
But, it's probably better just to explicitly set the urls.
Given two categories of entities, I'm selecting some kind of cross product set of them with the following URL definition:
url(r"^category1/(?P<category1>([0123456789]+,?)+)/category2(?P<category2>([0123456789]+,?)+)/$", view, {}, name="cross")
so basically URLs like /category1/1,2,3,4/category2/5,6,7,8/ are valid.
Now I introduced several views onto the same data, so that now I have URLs like /category1/1,2,3,4/category2/5,6,7,8/view1/ and /category1/1,2,3,4/category2/5,6,7,8/view2/. I would like to redirect the "old" URLs to view1. I haven't found anything easier than this:
url(r"^category1/(?P<category1>([0123456789]+,?)+)/category2(?P<category2>([0123456789]+,?)+)/$",
redirect_to, {
'url': lazy(lambda: reverse(
'cross_view1',
kwargs={
'category1': '111111',
'category2': '222222',
}
).replace('111111', '%(category1)s') \
.replace('222222', '%(category2)s'), str)(),
name="cross"}
The point here is that I want to reuse my matched groups in the URL, however, I cannot give them as kwargs to redirect_to, since they wouldn't be interpolated, and neither can I put verbatim formatting in the URL since it has to match my regex (comma-separated numeric IDs). So I introduce some unique ID (111111 and 222222 in this case) and replace them afterwards.
Obviously this feels, looks, smells and tastes very hacky. Is there any cleaner way to do this, apart from introducing an additional view and skipping redirect_to altogether?
You have some options here. You can use RedirectView, but I was unable to get it to reverse urls, since it looks like it runs before urlpatterns loads up. You can use it like so, tailor this to be used in your project:
from django.views.generic import RedirectView
Add this to your urlpatterns:
url(r'^(?P<location_id>\d+)/$', RedirectView.as_view(url='/statistics/dailyreport/%(location_id)s/')),
USE LAMBDA: This should work in most versions of django, I am using it in 1.6.5:
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
urlpatterns = patterns('',
....
url(r'^(?P<location_id>\d+)/$', lambda x, location_id: HttpResponseRedirect(reverse('dailyreport_location', args=[location_id])), name='location_stats_redirect'),
....
)
Just a quick note that in your reg exp you can use [0-9] instead of [0123456789]
I'm trying to use one app to satisfy multiple url paths. That is to say, I want the url /blog/ and /job/ to use the same app, but different views. There are a number of ways to do this I'm sure, but none of them seem very clean. Here's what I'm doing right now
# /urls.py
urlpatterns = patterns("",
(r"^(blog|job)/", include("myproject.myapp.urls")),
)
# /myapp/urls.py
urlpatterns = patterns("myproject.myapp.views",
(r"^(?P<id>\d+)/edit/$", "myproject.myapp.views.edit"),
(r"^(?P<id>\d+)/delete/$", "myproject.myapp.views.delete"),
(r"^(?P<id>\d+)/update/$", "myproject.myapp.views.update"),
(r"^insert/$", "myproject.myapp.views.insert"),
)
urlpatterns += patterns("",
(r"^(?P<object_id>\d+)/$", "django.views.generic.list_detail.object_detail", info_dict, "NOIDEA-detail"),
(r"^/$", "django.views.generic.list_detail.object_list", info_dict, "NOIDEA-community"),
)
# /myapp/views.py
def edit(request, type, id):
if (type == "blog"):
editBlog(request, id)
else (type == "job")
editJob(request, id)
def editBlog(request, id):
# some code
def editJob(request, id):
# some code
I've ended up breaking all of this into multiple model and view files to make the code cleaner but the above example doesn't account for things like reverse url lookups which breaks all of my template {% url %} calls.
Originally, I had blogs, jobs, events, contests, etc all living in their own apps, but all of their functionality is so similar, that it didn't make sense to leave it that way, so I attempted to combine them... and this happened. You see those "NOIDEA-detail" and "NOIDEA-community" url names on my generic views? Yeah, I don't know what to use there :-(
You can have more than one modules defining URLs. You can have /blog/ URLs in myapp/urls.py and /job/ URLs in myapp/job_urls.py. Or you can have two modules within a urls subpackage.
Alternatively you can manually prefix your url definitions:
urlpatterns = patterns("myproject.myapp.views",
(r"^jobs/(?P<id>\d+)/edit/$", "myproject.myapp.views.edit"),
(r"^jobs/(?P<id>\d+)/delete/$", "myproject.myapp.views.delete"),
(r"^jobs/(?P<id>\d+)/update/$", "myproject.myapp.views.update"),
(r"^jobs/insert/$", "myproject.myapp.views.insert"),
)
urlpatterns += patterns("",
(r"^blog/(?P<object_id>\d+)/$", "django.views.generic.list_detail.object_detail", info_dict, "NOIDEA-detail"),
(r"^blog/$", "django.views.generic.list_detail.object_list", info_dict, "NOIDEA-community"),
)
And then mount them as:
urlpatterns = patterns("",
(r"", include("myapp.urls")),
)
Personally I would go for more RESTful URL definitions though. Such as blog/(?P<post_id>\d+)/edit/$.
Looks pretty good to me. If you want reverse lookups, just have a different reverse name for each url format, even if they end up pointing to the same view.