passing additional arguments to django's login & template - django

My application uses Django's login view from django.contrib.auth.views.login, with a custom template I made.
I'd like to pass my template an additional argument which will change the login page in a few ways. Actually, I'm trying to show a different login page based on the URL.
How can I pass additional arguments to the login's view & to my custom template?

On Django 1.3 the login view accepts a new extra_context argument in the form of a dictionary.
django/contrib/auth/views.py
def login(request, template_name='registration/login.html',
redirect_field_name=REDIRECT_FIELD_NAME,
authentication_form=AuthenticationForm,
current_app=None, extra_context=None):

To add to Roberto Rosario's answer, you can pass a dictionary of arguments to extra_context inside your urls.py
yourProject/urls.py
from django.conf.urls import url
from django.contrib.auth import views
urlpatterns = [
url(r'^login/$', views.login, {
'template_name': 'your-template-dir/login.html',
'extra_context': {
'additional_arg1': val1,
'additional_arg2': val2,
...
},
}, name="login"),
...
]
Which will then be available in your template.
yourProject/your-template-dir/login.html
{% extends "your-template-dir/base.html" %}
<div>
<p>The value of 'additional_arg1' is {{ additional_arg1 }}.</p>
<p>The value of 'additional_arg2' is {{ additional_arg2 }}.</p>
</div>
Here's Django's official documentation.

The source shows there is only one place that would influence the template context.
def login(request, template_name='registration/login.html',
redirect_field_name=REDIRECT_FIELD_NAME,
authentication_form=AuthenticationForm): # here
So your only option is to hitch a ride on AuthenticationForm or write your own login view (which, by the way, is very simple if you look at the code).
from django.contrib.auth.forms import AuthenticationForm
AuthenticationForm.my_extra_data = 'foobar'
(r'^accounts/login/$', 'django.contrib.auth.views.login', \
{'template_name': 'myapp/login.html', \
'authentication_form': AuthenticationForm }),
Template
{{ form.my_extra_data }}

I was having an heck of time passing extra context variables from my custom login view-function to the template. They were all printing out as the empty string, despite following advice from a number of people a lot smarter than me.
Turns out my urls.py entry was still pointing to the built in login view, not my custom one.
Please don't let this happen to you.
When you eliminate the impossible, whatever remains, however improbable, must be the truth. -- Mr. Spock

Related

Django: Using named urls in #login_required decorator

Most of views in my django app use #login_required decorator. Also, I have three different login urls. Views have corresponding login urls hardcoded into their #login_required decorators.
#login_required('myapp/logintype1'):
def usertype1_home(request):
# Further dode
# ...
#login_required('myapp/logintype2'):
def usertype2_home(request):
# Further code
# ...
Since the number of such views is quite large, whenever I change login url in urls.py I have to change login-url in all the decorators. I want to use something like {% urls 'urlpatter1' %} and {% urls 'urlpatter2' %}. Can I use reverse?
How I can use named url patterns instead of hard coding url patterns in #login_required decorator?
Somewhere in the top of views.py after import ... statements add something like this
login_type1 = reverse_lazy('urlpatter1') # or LOGIN_TYPE1
login_type2 = reverse_lazy('urlpatter2') # or LOGIN_TYPE2
And use these variables later
#login_required(login_url=login_type1)
...
UPDATE: reverse was replaced with reverse_lazy as #Alasdair suggested. See docs (2nd point).

django admin redirect after a successful login

I am using Django=1.6.1.
Currently in my project, the admin system takes me to admin dashboard after a successful admin login (i.e: localhost:8000/admin/). From here I can visit most of my apps like users, categories, groups etc..
90% of the time I want to visit the users admin page (i.e localhost:8000/admin/my_project/users/). thus I am trying to find a way to redirect the admin login to admin users page rather than the dashboard. How can I do this?
I do not know if this is the right way to tackle this issue but this is how I have solved my problem.
I created a subclass of AdminSite called CustomAdminSite and overwritten the login() to accept a redirect url using the REDIRECT_FIELD_NAME(i.e using the 'next' hidden field option).
class CustomAdminSite(AdminSite):
#never_cache
def login(self, request, extra_context=None):
"""
Displays the login form for the given HttpRequest.
"""
from django.contrib.auth.views import login
context = {
'title': _('Log in'),
'app_path': request.get_full_path(),
REDIRECT_FIELD_NAME: settings.ADMIN_LOGIN_REDIRECT_URL,
}
context.update(extra_context or {})
defaults = {
'extra_context': context,
'current_app': self.name,
'authentication_form': self.login_form or AdminAuthenticationForm,
'template_name': self.login_template or 'admin/login.html',
}
return login(request, **defaults)
site = CustomAdminSite()
in urls.py
admin.site.login = path.to.custom.site.login
in settings.py
ADMIN_LOGIN_REDIRECT_URL = '/admin/my_project/users/'
P.S: please let me know if there is a better way to do this :)
Currently on Django (and since version 1.8) you can reverse your admin urls to redirect to any admin page.
For example:
The url you want to redirect to: /admin/my_project/users/
The reference: href="{% url 'admin:app_list' 'app_name' %}
The implementation: href="{% url 'admin:app_list' 'users' %}
Assuming you only have one model MyUser inside your users app, you can go directly into the admin list view of that model.
Reference: href="{% url 'admin:app_label_model_name_changelist' %}
Implementation: href="{% url 'admin:users_myuser_changelist' %}
You can do it by overriding the 'admin:index' url (the url named 'index' within admin namespace).

How do I install an app that doesn't have models or views?

I am attempting to install and test django-honeypot from sunlightlabs using the provided templates.
the application does not come accompanied with models nor views and I am confused as to how I am supposed to get the app to work. I have attempted to play with the urls to call the templates.
my mysite/mysite/urls.py follows:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
#url(r'^$', include('honeypot.urls')),
)
and my mysite/honeypot/urls.py follows:
from django.conf.urls.defaults import *
#from tagging.views import tagged_object_list
from django.conf.urls import patterns, include, url
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.shortcuts import render
from django.contrib import admin
urlpatterns = patterns('',
url(r'^$', render, dict(template_name='/home/mohrashak/attribute2/honeypot/templates/honeypot/webpage.html'), name='price'),
)
ROOT_URLCONF="honeypot.urls"
where webpage is
{% load honeypot %}
<form>
{% render_honeypot_field "field_name" %}
</form>
and my understanding is that something will be entered into the box be processed using the application. But there is no model. What am I missing?
You don't need to worry about models or views for django-honeypot. It is installed into the site-packages folder of your Python library so you don't have to write models/views/urls.py for you - these are all there in your pythonpath.
Make sure you read the online documentation for installing the app.
Here is the checklist:
Add honeypot to INSTALLED_APPS in settings.py;
Define HONEYPOT_FIELD_NAME which is the name to use for the honeypot field in your form. Try not to use 'honeypot' though as some bots will avoid it.
Make sure you add {% load honeypot %} in the top of your template using django-honeypot;
Then use the following tag in any form {% render_honeypot_field "field_name" %}
When your form is submitted - you can use the check_honeypot decorator from honeypot.decorators to check the value is present (or not by default) and correct.
Here is the example from the documentation to add to your view:
from honeypot.decorators import check_honeypot
#check_honeypot(field_name='hp_field_name')
def post_comment(request):
...
#check_honeypot
def other_post_view(request):
...
Edit1:
In response to your comment:
Yes;
Yes.
No - the nature of django-honeypot is that it prevents "form spam". So you have to have at least a form in your template. You should pass a form from a view to a template. More information in the documentation. I've written a near full example below.
Example of a contact form using django-honeypot
Note: This is untested.
This is an example that creates and shows a contact form (by going to a /contact/ URL) and then handles the form being submitted. Imagine we have used django_admin.py startapp contact_app
urls.py
Your urls file takes the /contact/ URL passes the request to our contact view.
from django.conf.urls import patterns, url
urlpatterns = patterns('',
url(r'^contact/$', 'contact_app.views.contact'),
)
contact_app/views.py
Your views file takes the request, and handles GET and POST requests. If the request is a GET it passes an empty form to the template. If a POST then it checks the form is valid and processes the form. We wrap it in the check_honeypot decorator to make sure it passes our django-honeypot test.
from contact_app.forms import ContactForm
from honeypot.decorators import check_honeypot
#check_honeypot
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render(request, 'contact.html', {
'my_form': form,
})
contact_app/forms.py
Here we specify the form fields that are going to be required and display in the template.
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
templates/contact.html
From our view, we pass our form (called my_form) to our template and use django's templating language to render each field as a <p></p>. We also load the honeypot and insert the necessary field.
{% load honeypot %}
<form action="/contact/" method="post">{% csrf_token %}
{{ my_form.as_p }}
{% render_honeypot_field "field_name" %}
<input type="submit" value="Submit" />
</form>

How to get user value in base.html?

I'd like to work with user data in main base.html which is my main template.
I actually use the login generic view. When I'm logged in on the homepage it gives me user information but in all other pages user is blank.
I've read many things about this: request, RequestContext, I've added django.contrib.auth.context_processors.auth to the processors list.
Any idea how to get this variable to show everywhere?
Two ways:
If you're using django 1.4+, you can use, render() when returning from your views:
def my_view(request):
...
return render(request, 'template.html', ctx)
If you're using older django or you don't like the render approach, you would return RequestContext using render_to_response, like this:
def my_view(request):
...
return render_to_response('template.html', ctx, context_instance=RequestContext(request))
Edit: sorry, forgot to actually answer question. If you're rendering your templates using one of the above ways and have:
django.contrib.auth in your INSTALLED_APPS
auth in midleware
auth in context_processors
(take a look at default settings.py file: https://github.com/django/django/blob/master/django/conf/global_settings.py )
Then you can use: {{ user }} and {{ user.is_anonymous }} to figure out what's going on.

Redirect to admin for login

I have a view defined for a url 'site/main/'. I would like to be able to have (unauthenticated) users redirected to the default '/admin/' page for login, then redirected to the '/main/' page after successful login. I followed the django documentation, but I must be missing something as I am unable to get this to work.
My view looks like:
def main(request):
if not request.user.is_authenticated():
return HttpResponseRedirect('admin/?next=%s' % request.path)
else:
I get an error:
Page not found (404)
Request Method: GET
Request URL:http://sitename:8080/main/admin/?next=/main/
Any help is greatly appreciated !
You're missing an initial / in the URL: /admin/?next=...
However this still won't work, as the admin URL doesn't know anything about the next parameter. That's only for the actual login views. With your code, the user will be logged into the admin but will not be redirected back to your page.
You should build a login template and wire it up to the built-in login views. Then instead of checking is_authenticated in the view, you should just use the login_required decorator.
#login_required
def main(request):
...
Your request.path shouldn't be /main/. Try it without the first.
If you want to redirect to admin for login for specific view, and then to redirect back to the view url after successful login you only need to do two things:
Add LOGIN_URL to settings.py inside your django project module:
...
LOGIN_URL = '/admin/login/'
Add #login_required as decorator to your view function inside views.py:
from django.contrib.auth.decorators import login_required
...
#login_required
def main(request):
Once you set LOGIN_URL = '/admin/login/' you can use #login_required on whatever view in entire django project and it will redirect to admin for login and after successful login will redirect back to the view url.
Also now you don't need to use is_authenticated any more inside of a view as Daniel Roseman already said.
The good thing is that now you also don't need to build a login template and wire it up to the built-in login views.
What is also good with this approach is the you have flexibility to easily add or remove this kind of authentication to whatever view you want.
urls.py:
url('^', include('django.contrib.auth.urls')),
registration/login.html:
<h3>Login foo</h3>
<form method="post" action="">
{% csrf_token %}
{{form.as_p}}
<input type="submit" value="Login">
</form>
views.py
def only_for_users(request):
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/?next=%s' % request.path)
// fetch some really interesting data
env=(django.get_version(),settings.BASE_DIR,sys.version)
envMod=collections.OrderedDict(sorted(sys.modules.items()))
return render(request,'env.html',{'env':env, 'envMod':envMod})
It works for Django 1.6 and uses the built-in login (look at the urls.py) and template. So you do not need to build a view function.
Info on urls