Django dynamic urlpatterns - python

I'm building a simple web application with Django. My users are separated into multiple groups, for example Group A, Group B, etc.
What I want to do is to dynamically update urlpatterns list in urls.py so that I can have different views on same url endpoints.
For example, I'd like to do something like this (I know syntax is off, it's just to demonstrate what I want)
urlpatterns = [
url(r'^$', views.homepage, name='homepage'),
url(r'^login/$', views.BaseLogin.as_view(), name='core.login'),
url(r'^logout/$', views.logout, name='core.logout'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if request.user in groupA:
urlpatterns.append(url(r'^dash/', include('groupA.urls')))
else:
urlpatterns.append(url(r'^dash/', include('groupB.urls')))
How would I best achieve this?

I think this is neither possible nor desirable. You should place such logic in the view. Make both land in the same view and redirect or simply put together different content in the view based on the user's group affiliation.

It's not a good idea to change urlpatterns dynamically, but you could create two url confs mysite/groupA_root_urls.py and mysite/groupB_root_urls.py.
You can then write a middleware that sets the request.urlconf attribute to either 'mysite.groupA_root_urls' 'mysite.groupA_root_urls'.
Django will then use that urlconf instead of the conf from the ROOT_URLCONF setting.

URLs are not loaded dynamically for every user, they are parsed and loaded on application startup, so you cannot put per-request logic in there. In general, this logic should be handled in your view.
That said, you can simulate this behavior using custom middleware. Make a middleware class, write process_view() method to check your URL and if it one you are interested in, find and run the view function yourself and return the HttpResponse. Make sure your middleware is the last in the list, so every other middleware gets a chance to run before yours does. Mind you that this would fall under the "ugly hack" category in any serious project :)
Here is a link to relevant docs https://docs.djangoproject.com/en/1.9/topics/http/middleware/#process-view

Related

Django: Temporarily redirect all URLs to one view

I am building site and thought it would be nice to have some sort of maintenance page where I could redirect users if need be.
How can I start redirecting all requests to one specific view? I am using Constance to have maintenance switch in my admin view (just a bool value). Its value is then available thorough the project.
I've already prepared another list of urlpatterns but cannot figure out how to dynamically change them so the redirect works.
maintenance_urlpatterns = [
url(r'^$', views.maintenance, name='maintenance'),
]
This is urls.py file from app, not the project one which I would leave alone.
I also thought about modifying base.html template and rendering "maintenance page" this way, but I think that is pretty bad solution.
EDIT: To clarify. My main question isnt how to write urlpattern that will capture all traffic, but how to edit these dynamically in response to Constance config change. So I can toggle that in admin.
The regex in your question will only match on / (the root of your site). If you want to catch everything, just use ^ only. Every URL has a beginning, so ^ always matches.
url(r'^', views.maintenance, name='maintenance')
Of course, this depends upon your normal urlpatterns being disabled in some fashion. It sounds like maybe you have that covered already.
Implementing maintenance page in django
add a bool variable in settings.py like this:
MAINTENANCE_MODE = True
and in main urls.py file, after all configurations add these lines:
from django.urls import re_path
from django.conf import settings
from django.views.generics.base import TemplateView
if settings.MAINTENANCE_MODE:
urlpatterns.insert(0, re_path(r'^', TemplateView.as_view(template_name='../path/maintenance.html'), name='maintenance'))
if you want turn off the maintenance mode, you have to make MAINTENANCE_MODE variable False and restart the web-server.

Multiple Django apps sharing the same URL patterns

I have multiple apps that share almost the same URL patterns, defining almost the same views. Currently i have a urls.py for each app to serve its routing. What i want to do is to group the similar patterns into a single shared_urls.py file then use it in those apps.
To make it easy to understand, suppose (just an example here) i have a blog app and an archive app.
They both define a pattern and view for /post, /comment and /user. So instead of each of them having its own urls.py defining the same pattern, i want to define these patterns in one place then use it in each of the apps, while loading the correct app view.
Current Vs Wishing
Current
project urls.py
url(r'^blog/', include('blog.urls')),
url(r'^archive/', include('archive.urls')),
blog urls.py
url(r'^post/', views.post),
url(r'^comment/', views.comment),
url(r'^user/', views.user),
archive urls.py
url(r'^post/', views.post),
url(r'^comment/', views.comment),
As you see the two apps share almost the same patterns but each has its own implemented view.
Wishing
project urls.py
url(r'^blog/', include('blog.urls')),
url(r'^archive/', include('archive.urls')),
shared_urls.py
#How to bind with the correct app's view!
url(r'^post/', views.post),
url(r'^comment/', views.comment),
blog urls.py
url(r'^user/', views.user),
url(r'', include(shared_urls)),
archive urls.py
url(r'', include(shared_urls)),
This is a logic (controller/view) matter, not a url (routing) matter. Sharing URLs would be a bad practice. They should be clearly separately defined since they provide different actions. The fact that you want to use the same logic/content behind two different url must be handled in your views.

Django alias redirect on wrong url to the correct url

The question may have been asked many times, but I couldn't find something about this which is a basic stuff to do. I have a problem with my website one of the sub urls of google search redirects to the wrong page.
Here are the urls :
www.example.com/login/user/ #wrong url
www.example.com/accounts/login/ #correct url
urls.py : url(r'^accounts/login/$', auth_views.login, name='login'),
I'd like the user to be redirected to the correct url if he access the wrong one. I know that it is possible to do that with views using redirect(reverse('...')) but doing it just for this purpose doesn't sound like the best way.
Is there a way to redirect an user when he enters a wrong url to another one only by using urls.py ?
You would normally do this on your webserver, e.g. Apache
RewriteRule /login/user/ /accounts/login/ [R=301,L]
That way you don't need to load Django to do a static redirect (you might build up a longer list of these as site-designs evolve).
One solution is to use the RedirectView inside urls.py as shown here in the docs
So, for example:
url(r'^login/user$', RedirectView.as_view(pattern_name='login')),
To urls.py, you can add:
url(r'^login/user/$', auth_views.login, name='login')
It seems like you are aware of that. Did I not understand your question?

django-allauth configuration doubts

I'm using django-allauth with Django 1.5.1 and I have a few questions when setting it up:
1. Configure urls.py
The docs says that you have to add the following to urls.py file:
urlpatterns = patterns('',
...
(r'^accounts/', include('allauth.urls')),
...
)
The problem is that I already have a custom app called accounts and I already use the following URL pattern:
(r'^accounts/', include('accounts.urls')),
So I have a naming collision here with the accounts/ regex URL. My question is: can I rename the allauth URL pattern to (r'^auth/', include('allauth.urls')) without having problems, or is it unsafe to do so and it'd be better to rename my own URL to something like (r'^users/', include('users.urls')) (and rename my accounts app to users for naming consistency).
2. Customize allauth default templates
What is the proper way to customize the default templates for login, etc.? I think that modifying the library directly is not the best approach. I guess it should be done through templates directory using some concrete directory hierarchy. Also, I don't know if some kind of base.html file must be provided to extend from when overriding these templates or the site's base.html that all pages extend can be used without problems. Could you illustrate me with this?
3. Admin login form shows logins and logouts the first time it's accessed
When I access the admin panel after some logins and logouts the history appears, but if I refresh the page then it disappears. I think this must be something related with the django messages:
4. Setting SOCIALACCOUNT_PROVIDERS
Is the dictionary setting called SOCIALACCOUNT_PROVIDERS optional or must it be set?
5. How is the password calculated when a user signs in with a 3rd party app?
When the user is created it has a password, but how is it calculated? And... is it useful or is it only a placeholder for this required field? Can the user use it to local login?
Thanks!
With respect to 1):
There is no collision as long as there is no overlap in the fully matched URL patterns. For example: if your accounts app has a match for "/accounts/login/" then there is indeed a collision as allauth is gunning for that URL as well. But, if your accounts app simply matches other URLs with /accounts/ as prefix then you are fine.
If you insist, you can indeed put allauth URLs below a different path. allauth uses name based URL reversal, so the new path prefix will be picked up automatically.
As for 2):
There is nothing special about allauth templates. You can override them just like you would for any other Django app.
Have a look at the example app. It has both Bootstrap and uniform template overrides. They can be enabled by uncommenting this line: https://github.com/pennersr/django-allauth/blob/901485557d4ddee30fed920f2159cdf499c39e1c/example/example/settings.py#L126
All allauth templates inherit from a base template, called base.html. I would expect that your project also has a base template. Either override the base.html with yours, or, override base.html with a template that extends from yourbase.html
3): allauth uses the Django messages framework. See:
https://docs.djangoproject.com/en/dev/ref/contrib/messages/#expiration-of-messages -- if you do not iterate over the messages in order to display them, they do not expire. So apparently you are not showing the messages in your templates. Therefore, they heap up until the admin appears which renders (and clears) all messages collected so far...
4) Optional
5) There is no password set, meaning, the user can only login using the 3rd party account until he actually sets a password (/accounts/password/set/).

Django authentication on a per-app basis

I am getting started with Django and I found something I don't understand very well when it comes to user authentication (using the typical django.contrib.auth mechanism).
When I create a "site" using
django-admin.py startproject mysite
I understand I create something like a "server". Then, I need to create an app (as explained in the "getting started" tutorial). I create my models for the app, not for the server. The idea I have (which may be totally wrong) is that apps are something fairly independent among themselves (pretty isolated from each other). I create my models for each app, there is a different set of "views" per app, etc.
But when it comes to user management, that managing seems to be global for the whole project (or "server"): All the settings are specified through the global settings.py file, the built-in login pages are stored in the global templates/ directory... What if I have two apps and I want the login pages for them to have a different aspect? What if after a successful login, I want to redirect to an specific page of app#1 or to another specific page of app#2 depending on the app the user was trying to log in? Maybe I am missunderstanding the whole concept behind a Django app?
Basically, I have a mystartapp application inside a project called myserver. I am using the built-in login view, sending a form to "django.contrib.auth.views.login". With the default configuration, a successful login was trying to redirect me to http://127.0.0.1:8000/accounts/profile/, which I don't have.
I could find a workaround by editing the global settings.py file and the global urls.py files:
---------- settings.py ----------
[ . . . ]
# User Logging in Settings
LOGIN_URL = '/login'
LOGIN_REDIRECT_URL = '/'
[ . . . ]
---------------------------------
and
------------ urls.py ------------
urlpatterns = patterns('',
# Examples:
# url(r'^$', 'myserver.views.home', name='home'),
# url(r'^myserver/', include('myserver.foo.urls')),
# Uncomment the admin/doc line below to enable admin documentation:
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
#The following line will include the urls for the "mystartapp" application
url(r'^s', include('mystartapp.urls', namespace="mystartapp")), #Dirty trick
url(r'^mystartapp/', include('mystartapp.urls', namespace="mystartapp")),
# Uncomment the next line to enable the admin:
url(r'^admin/', include(admin.site.urls)),
url(r'^login/$', 'django.contrib.auth.views.login'),
)
------------------------------
This way, thanks to the 10th line (the one with the #'Dirty trick' comment), everything that goes to the root of 127.0.0.1:8000/ will be redirected to the same set of urls as if I used 127.0.0.1:8000/mystartapp/, which combined with the LOGIN_REDIRECT_URL = '/' option in settings.py, will end up making the browser to redirect to 127.0.0.1:8080/mystartapp/index.html, after a correct login but that doesn't seem "clean".
Is there a better way of establishing a redirection to 127.0.0.1:8080/mystartapp/index.html? Is the dirty trick really a dirty trick or is it the way it's supposed to work? Am I missing something here? (I'm pretty sure the answer to this last question is "Yes!", though)
Thank you in advance.
Is there a reason why using LOGIN_REDIRECT_URL = '/mystartapp/index.html' wouldn't work in your case?
Regarding the concept of apps, they are supposed to be independent bits of functionality (which is why they are sometimes labelled "reusable"). However, once you tie apps into a project, the goal is that they do work together at some point !
In the case of contrib.auth, this reusability is expressed by the parameters you used (such as LOGIN_URL or LOGIN_REDIRECT_URL), which allows you to configure the behavior of the app so it works in your project.
For instance, for a merchant side, you could use several apps to perform different actions related to your project:
An app with your actual functionality (products, pages...)
An app for search (like django-solr)
An app for registration (django-registration)
Contrib apps for authentication, session
A merchant app for payments
The apps perform different bits of functionnality, but they all serve the same purpose and make up a project together.
If you need the apps to operate in a totally independent manner, they shouldn't be part of the same project in the first place! (Although you could use app A in projects P and Q, and app B only in P, for example).

Categories