Django authentication on a per-app basis - python

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).

Related

Django admin unregister Sites

I've been trying to unregister the admin for sites in django by doing the following:
from django.contrib.sites.models import Site
admin.site.unregister(Site)
However this gives me an error stating that "Site" is not registered (even though it showed up in admin before).
If I try doing the following I get no errors but "Site" stays in the admin:
from django.contrib.sites.models import Site
admin.site.register(Site)
admin.site.unregister(Site)
I need the sites app and cannot take it out of INSTALLED_APPS in settings. However the admin for it is utterly useless to me.
Any ideas on what I am doing wrong here?
Thanks!
The order of your INSTALLED_APPS setting is important.
When Django starts it will import the applications in INSTALLED_APPS in the order they are defined (https://docs.djangoproject.com/en/1.11/ref/applications/#how-applications-are-loaded). In your example above you were unregistering Site before Django had the chance to register is it.
There isn't much you can do in terms of troubleshooting except for reading the logs very carefully. After staring at them for a while you either become one with Django and understand it all, or you come here like the rest of us ;-)
My INSTALLED_APPS usually start with the django.contrib apps, then any 3rd-party applications, then my apps at the bottom. I only change this if I have a very good reason to.

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.

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/).

Include Django Project specific objects in Django package

I'm developing a web-app with common components and decided to pack those common components (some views, templates, logic) in a Django package.
Now the problem is: I want to access variables of my specific Django project, depending on the project, from the package. In this specific instance:
My Django package has an admin panel. A custom-made admin panel that allows me to manage a list of algorithms. However, these algorithms change. In one specific Django project I might have 3 algorithms, in another one I might have 5.
The admin panel in the Django package allows me to edit custom algorithm components, such as weights, test cases, etc.
Now my problem is, in the Django view, in the package, I want to access the Django project's algorithms in order to show them. As a list, for selection/editing/deleting, but also to view them in detail and edit them.
Now my problem is, obviously, I can't include something from the Django project in the Django package, otherwise it would become a dependency. And the algorithms are different and unique in each specific project.
So, in short, the question is how to access specific data of a Django project by a package used by that project, without making the package dependent of anything.
OK, so after some more investigation I found a possible solution for this. Will leave it here for informational purposes while waiting for an answer.
Instead of loading the package URLs as a module I use a custom function that passes the Django project's variables to the package URLs, and therefore, the views.
So in the project urls.py, instead of:
url(r'^package/$', include('package.urls'))
I use
url(r'^package/$', obtain_package_urls(custom_content))
where obtain_package_urls() is a function in the package:
def obtain_package_urls(custom_content):
urlpatterns = patterns('',
url(r'^url1$', view1, {'custom_content': custom_content},
url(r'^url2$', view2, {'custom_content': custom_content},
)
return (urlpatterns, None, None)
The goal is to, instead of the include function that includes the static URL-view mapping of Django, to use this function that returns a dynamic URL-view mapping with the custom content included in the views.
So in effective terms with the algorithms I ended up doing:
#Django project settings.py
obtain_algorithm_list():
return Algorithm.objects.all()
#Django project urls.py
from project.settings import obtain_algorithm_list
urlpatterns = ...
...
url(r'^package/$', obtain_package_urls(obtain_algorithm_list())
...
#In the package urls.py
urlpatterns = ...
url(r'^view1/$', view1, {'algorithms': algorithms},
...
#And then in each package view
def view(request, algorithms=[]):
...
use_for_something(algorithms)

How to configure the webserver to server a particular page as the default page?

I am using Lighttpd and Django. I have configured my Lighttpd server to pass all the requests ending with ".psp" extension to Django.
My startup page is a page served through Django, which is accessed as "http://192.168.1.198/home.psp". I want to enable the user to browse this page without writing "home.psp" explicitly in the url i.e. using "http://192.168.1.198"
Is this possible?
Thanks for any help in advance.
I think you're confusing concepts here between the "old" method of having individual files represent web pages which themselves contain code that is passed off to an interpreter before being sent in a response to how django/frameworks work.
If you're familiar with apache, imagine django as in part taking on the role of mod_rewrite. Django, and other frameworks, have what's called a dispatcher, or routing, mechanism.
Basically, they subscribe to the MVC pattern that says you should separate out the model, controller and view (in django parlance, model, template and view).
Now what then happens is you have a file called urls.py in django, which contains a list of routes (urls) and names of methods (usually contained in views.py) which handle them. Here's an example:
urlpatterns = patterns('',
url(r'^dologin$', 'testapp.views.auth_login', name="auth-login-uri"),
url(r'^doopenidlogin$', 'testapp.views.auth_openid_login', name="auth-openid-login-uri"),
url(r'^dologout$', 'testapp.views.auth_logout', name="auth-logout-uri"),
url(r'^login$', 'testapp.views.loginform', name="login-form"),
url(r'^openidlogin$', 'testapp.views.openidloginform', name="openid-login-form"),
url(r'^$', 'testapp.views.index', name="index"),
)
Here testapp is a python package, views.py is a python file and index is a django view. The url is constructed from regex, so I can have whatever I want as the url, much how stackoverflow urls are formed.
So basically, you never need file extensions again. I'd strongly suggest getting a good book on django - there are a few around.
What you might be looking for is the index-file.names directive in Lighty's configuration file. Just add "home.psp" to the list in your configuration file, and Lighty will look for it when no filename is specified.
I solved the problem myself. Here is what I did:
I had to add a statement inside url.rewrite-once block of lighttpd's configuration file like:
url.rewrite-once = (
"^(/media.*)$" => "$1",
"^(/static.*)$" => "$1",
"^/favicon\.ico$" => "/media/favicon.ico",
"^(/)$" => "/my_project_dir/home.psp",
"^(/.*)$" => "/my_project_dir$1",
)
Apart from this, I added the following line in my urls.py:
(r'^$',my_index_view_name),
Hope this helps someone in the future. Thanks everybody for your replies above. Cheers!

Categories