Django context processor and csrf_token - python

I have a login form that I want to be available in all my views, so I created a context processor to add this form to every loaded context.
The problem is that {% csrf_token %} on the form template won't render the hidden input tag with the CSRF token value.
This is the context_processor order in settings.py:
TEMPLATE_CONTEXT_PROCESSORS = (
'django.contrib.auth.context_processors.auth',
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
'django.core.context_processors.media',
'django.core.context_processors.static',
'django.core.context_processors.tz',
'django.contrib.messages.context_processors.messages',
'django.core.context_processors.request',
'django.core.context_processors.csrf',
'absolute.context_processors.absolute',
'myproject.app.context_processors.base',
)
And then the processor itself on app/context_processors.py:
from django.contrib.auth.forms import AuthenticationForm
def base(request):
context = dict()
if not request.user.is_authenticated():
context['login_form'] = AuthenticationForm()
return context
The form template:
{% load i18n %}
<form method="post" action="{% url "django.contrib.auth.views.login" %}">
{% csrf_token %}
<input type="hidden" name="next" value="{% if request.GET.next %}{{ request.GET.next }}{% else %}{{ request.get_full_path }}{% endif %}" />
{{ login_form.as_p }}
<input type="submit" class="button success expand" value="{% trans 'Login' %}" />
</form>
The HTML output for this form:
<form action="/accounts/login/" method="post">
<input type="hidden" value="/" name="next">
<p><label for="id_username">Usuário:</label> <input type="text" name="username" maxlength="254" id="id_username"></p>
<p><label for="id_password">Senha:</label> <input type="password" name="password" id="id_password"></p>
<input type="submit" value="Login" class="button success expand">
</form>
And the error I get when submitting it:
CSRF verification failed. Request aborted.
However, and as I'm only using class-based views, if I add a csrf_protect decorator the form will work, but like this I would have to declare the dispatch method in all my views:
from django.views.decorators.csrf import csrf_protect
class HomeView(TemplateView):
template_name = 'home.html'
#method_decorator(csrf_protect)
def dispatch(self, *args, **kwargs):
return super(HomeView, self).dispatch(*args, **kwargs)
Problem status
I gave up from putting the AuthenticationForm on all my views by creating a login form page. Anyway, it would still be awesome if someone could help me find a solution for this problem.

I've spent a couple of hours fighting an issue similar to the one you've described here. The {% csrf_token %} wasn't rendering anything and I was seeing this when in debug mode:
defaulttags.py:66: UserWarning: A {% csrf_token %} was used in a template, but the context did not provide the value. This is usually caused by not using RequestContext.
I was using a simple view that inherited from a TemplateView:
class MenuIndexView(TemplateView):
template_name = 'menu/index.html'
def get_context_data(self, **kwargs):
kwargs = super().get_context_data(**kwargs)
session = self.request.session
kwargs['zip_code'] = session.get('zip_code')
kwargs['calendar'] = helpers.get_menu_calendar(date.today() + timedelta(days=1), timedelta(days=14))
kwargs['forms'] = {'zip_code': forms.ZipCodeForm({'zip_code': session.get('zip_code')})}
return kwargs
After fussing around a bit under Django's I realized that pretty much no context at all was available where the tag was being generated (CsrfTokeNode on Django's defaulttags.py file):
class CsrfTokenNode(Node):
def render(self, context):
csrf_token = context.get('csrf_token', None)
if csrf_token:
if csrf_token == 'NOTPROVIDED':
return format_html("")
else:
return format_html("<input type='hidden' name='csrfmiddlewaretoken' value='{}' />", csrf_token)
else:
# It's very probable that the token is missing because of
# misconfiguration, so we raise a warning
if settings.DEBUG:
warnings.warn(
"A {% csrf_token %} was used in a template, but the context "
"did not provide the value. This is usually caused by not "
"using RequestContext."
)
return ''
In this point of the code I was only seeing an item within the context, with a zip_code key.
I opened up the main template file and realized that I was making a rookie mistake - this was my main template menu/index.html:
{% extends 'base.html' %}
{% block content %}
<div class="menu-order-meta zip_calendar">
<div class="ink-grid align-center">
<div class="column-group gutters half-vertical-padding medium">
{% include 'partials/menu/zip-code.html' with zip_code=zip_code only %}
</div>
</div>
</div>
{% endblock %}
I was including the form through a partial template and I was explicitly restricting the available context within that partial template - notice the with zip_code=zip_code only declaration - (and by doing so, implicitly turning the csrf_token context processor unavailable).

Maybe are you missing django.middleware.csrf.CsrfViewMiddleware in MIDDLEWARE_CLASSES ?
MIDDLEWARE_CLASSES = (
....
'django.middleware.csrf.CsrfViewMiddleware',
....
)

Related

Can't be redirected after form action in Django

I created a form for creating a new item in Django. The app should be redirected to item_new but it's not working. This below error occurs.
Page not found
http://127.0.0.1.8000/item/new
show.html
<form
action="{% url 'item_new' %}"
method="POST"
enctype="multipart/form-data"
>
{% csrf_token %}
<div class="formItem">
<label>Name</label>
<input type="name" name="name" />
</div>
<div class="formBtn">
<input type="submit" class="btn btnPrimary" value="Submit" />
</div>
</form>
urls.py
urlpatterns = [
path('item/<slug:slug>/', views.item_detail, name='item_detail'),
path('item/new/', views.item_new, name='item_new'),
]
views.py
def item_detail(request, slug):
item = get_object_or_404(Item, slug=slug)
return render(request, 'item/show.html', {'item': item})
def item_new(request):
return redirect('home')
In your urls.py file, change the order of lines. Like this:
urlpatterns = [
path('item/new/', views.item_new, name='item_new'),
path('item/<slug:slug>/', views.item_detail, name='item_detail'),
]
The order of urls, does matter. In your current situation, requests made to http://127.0.0.1.8000/item/new will be sent to item_detail view function instead of item_new.

Django Model form don't update database records

I am new in django and I want to create a form to update some database entries.
this is a simple form where I have a simple input text where I write the id of the record that I want to update:
main.html
<form method='post' action='/config/{{ idprov }}' >
<input type="text" class="form-control" name="idprov" id="idprov" value ="{{ idprov }}" />
<input class="btn btn-danger" type="submit" value="Config">
</form>
forms.py
from django import forms
from .models import Proveedor, Estado, Config
class ConfigForm(forms.ModelForm):
class Meta:
model = Config
this is my views.py:
def configView(request,idprov):
prov = Config.objects.get(idproveedor=idprov)
if request.method == 'POST':
form = ConfigForm(request.POST or None, instance=prov)
if form.is_valid():
form.save(commit=false)
return HttpResponseRedirect('/monitor/')
else:
form = ConfigForm( instance=prov)
return render_to_response('config.html',
{'form':form})
my template config.html:
{% extends 'base.html' %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-4">
<form method='POST' action='' >
<div class="form-group">{% csrf_token %}
{{ form.as_p }}
</div>
<button type='submit' class="btn btn-primary">Grabar</button>
</form>
</div>
</div>
</div>
{% endblock %}
my urls.py:
from django.conf.urls import patterns, include, url
from prov.views import home
from prov.views import proveedores
from prov.views import estado
from prov.views import monitorViewV2
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
# Examples:
url(r'^$', 'prov.views.home', name='home'),
url(r'^proveedores/$', 'prov.views.proveedores', name='proveedor'),
url(r'^estado/$', 'prov.views.estado', name='estado'),
url(r'^monitor/$', 'prov.views.monitorView', name='monitor'),
url(r'^monitorv/$', 'prov.views.monitorViewV2', name='monitorv2'),
url(r'^config/(?P<idprov>\d+)/$', 'prov.views.configView',name='config'),
url(r'^admin/', include(admin.site.urls)),
)
But I receive the error Page not found.
I try to follow other snippets and always raise different errors.
I think the error is in the urls.py and in the way I pass the argument from the main.html.
Any advice or snippet will be very appreciated
Thanks in advance
First you have a form.save(commit=false) in your view without saving it later, thats mean the data is never saved into the model.
Second in your template config.html the form action is empty <form method='POST' action=''> change for <form method='POST' action='.'>
I hope that help to you.

Django render_to_response not working as expected

Django 1.5 - django-ldap-auth
Trying to figure out how to properly using request_to_context as it only works on the login.html page itself. (user.is_authenticated etc).
After the user gets logged in, it forwards the user to /reboot. From there I want to use {{ user.is_authenticated }} / {{ user.username }}, but nothing shows up(true or false). It is just blank.
On the login.html page itself they work correctly. If I render to the reboot/index.html it does not render the code that I have the index do. I assumed I would be able to call these commands after a user is authenticated. Hopefully I am just missing a small piece to this puzzle.
I have included all of the necessary code. Let me know if you need something else.
Here is my layout.
reboot/form.py
from django import forms
from django.contrib.auth.models import User
from django.forms import ModelForm
class LoginForm(forms.Form):
username = forms.CharField(label=(u'User Name'))
password = forms.CharField(label=(u'Password'), widget=forms.PasswordInput(render_value=False))
views.py
from django.template import Context, loader, RequestContext
from django.shortcuts import render_to_response
from django.contrib.auth.decorators import login_required
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User
from django.http import HttpResponse, HttpResponseRedirect
from servers.models import Server
from reboot.forms import LoginForm
import paramiko
import socket
def logout_user(request):
logout(request)
return HttpResponseRedirect('/status/')
def index(request):
t = loader.get_template('reboot/index.html')
servers = Server.objects.all()
c = Context( {
'servers': servers,
})
return HttpResponse(t.render(c))
def test_ssh_liveness(ssh, name):
try:
ssh.connect(name, timeout='1')
return True
except socket.timeout:
# server is down
return False
except socket.gaierror:
# invalid server name
return False
except paramiko.SSHException:
# unknown host key
return True
def server(request, name):
ssh = paramiko.SSHClient()
is_alive = test_ssh_liveness(ssh, name)
return HttpResponse("You selected server "+name+" and it is "+str(is_alive))
# user login
def login_user(request):
if request.user.is_authenticated():
# return HttpResponseRedirect('/reboot/')
pass
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return HttpResponseRedirect('/reboot/')
else:
return render_to_response('login.html', {'form', form}, context_instance=RequestContext(request))
else:
''' user is not submitting the form, show the login form '''
form = LoginForm()
context = {'form': form}
return render_to_response('login.html', context, context_instance=RequestContext(request))
reboot.urls
from django.conf.urls import patterns, url
from reboot import views
urlpatterns = patterns('',
url(r'^$', views.index, name='index'),
#(r'^$', views.login_user),
url(r'^server/(?P<name>[^/]+)', views.server, name='server')
)
main url file
from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),
url(r'^reboot/', include('reboot.urls')),
# Login for reboot
(r'^login/$', 'reboot.views.login_user'),
(r'^logout/$', 'reboot.views.logout_user'),
url(r'^status/', include('status.urls')),
)
login.html
% extends "base.html" %}
{% block content %}
<p> print {{ user.is_authenticated }}</p>
<div id="login">
<form class="form-horizontal" name="LoginForm" action="/login/" method="post">
{% csrf_token %}
{% if next %}
<input type="hidden" name="next" value="{{ next }}" />
{% endif %}
<div class="control-group">
<label class="control-label" for="username">Username</label>
<div class="controls">
<input type="text" id="username" name="username" placeholder="Username">
</div>
</div>
<div class="control-group">
<label class="control-label" for="password">Password</label>
<div class="controls">
<input type="password" name="password" id="password" placeholder="Password">
</div>
</div>
<div class="control-group">
<div class=controls">
<button type="submit" class="btn">Login</button>
</div>
</div>
</form>
</div>
{% endblock %}
/reboot/index.html
{% extends "base.html" %}
{% block title %}Server{% endblock %}
{% block content %}
<h2>Server Control Panel</h2>
<h4>Welcome, print {{ user.is_authenticated }}.</h4>
{% if user.is_authenticated %}<p>Logout</p>{% else %}Login</p>{% endif %}
<p class="lead">
The servers below correspond to servers that you have access to.
If you believe there are errors, please contact me.
</p>
<div class="container-fluid">
<table class="table table-hover">
<thead>
<tr>
<th><h4>Hostname</h4></th>
<th><h4>Commands</h4></th>
</tr>
</thead>
<tbody>
{% for server in servers %}
<tr>
<td>{{ server.hostname }}</td>
<td>
start
restart
shutdown
</td>
</tr>
{% endfor %}
</tbody>
</table>
</table>
</div>
{% endblock %}
I appreciate all of your help. I have been stuck on this issue for a few days now and have not been able to figure it out.
John
Your view needs a RequestContext instance (docs), like your login view, so it knows which user is logged in (and other stuff). So instead of:
def index(request):
t = loader.get_template('reboot/index.html')
servers = Server.objects.all()
c = Context( {
'servers': servers,
})
return HttpResponse(t.render(c))
just make it:
def index(request):
servers = Server.objects.all()
context = {
'servers': servers,
}
return render_to_response('reboot/index.html', context, context_instance=RequestContext(request))
EDIT
It's not really about the render_to_response. As the import says, that's just a shortcut.
Django's context processors (basically, functions that add variables to your template context) are request-based, and therefor are only called if you specify a RequestContext, and that includes django.contrib.auth.context_processors.auth, the context processor that includes the user variable into your template.
The equivalent of your code would be:
def index(request):
t = loader.get_template('reboot/index.html')
servers = Server.objects.all()
c = RequestContext(request, {
'servers': servers,
})
return HttpResponse(t.render(c))

How to insert data to a template after render ? (django)

I am making a decorator to insert verification code into a template. The scenario is the following :
#insert_verification
def my_view(request):
# View code here...
return render(request, 'myapp/index.html', {"foo": "bar"},
content_type="application/xhtml+xml")
def insert_verification(func):
def wrapped(request):
res = func(request)
if type(res) == HttpResponse:
# add a verification code to the response
# just something like this : res.add({"verification": 'xxxxx'})
# and varification can fill in the template
return res
return wrapped
I use the following template:
{% block main %}
<fieldset>
<legend>{{ title }}</legend>
<form method="post"{% if form.is_multipart %} enctype="multipart/form-data"{% endif %}>
{% fields_for form %}
<input type="hidden" value="{{varification}}" >
<div class="form-actions">
<input class="btn btn-primary btn-large" type="submit" value="{{ title }}">
</div>
</form>
</fieldset>
{% endblock %}
It seems I should render a template twice with different dictionary. But I don't know how to do that.
I think the better approach will be to implement your context processor to add verification context variable to the template contexts.
For example:
verification_context_processor.py
def add_verification(request):
#get verification code
ctx = {'verification': 'xxxxx'}
#you can also check what path it is like
#if request.path.contains('/someparticularurl/'):
# add verification
return ctx
In settings.py, update
import django.conf.global_settings as DEFAULT_SETTINGS
TEMPLATE_CONTEXT_PROCESSORS = DEFAULT_SETTINGS.TEMPLATE_CONTEXT_PROCESSORS + (
'custom_context_processors.add_verification',
)
You view should use RequestContext while rendering the response.
def my_view(request):
# View code here...
return render_to_response(request, 'myapp/index.html', {"foo": "bar"},
context_instance=RequestContext(request)
)

How to redirect django.contrib.auth.views.login after login?

I added django.contrib.auth.views.login everywhere in my webpage, for that I had to load a templatetag (that returns the AuthenticationForm) in my base.html. This templatetags includes the registration/login.html template.
The login is working ok but I want it to redirect the users to the same page they are before login. Now, it redirects me to /wherever_i_am/login wich shows registration/login.html with the 'login ok' or 'login fails' messages but without the rest of base.html.
I have followed django documentation and a few SO questions like this but I cannot redirect correctly. I have modified the next variable but it doesn't seem to work (next={{ request.get_full_path }} redirects me to /wherever_i_am/login ...again)
Have you tried something similar? any ideas?
UPDATE1
Now, the question could be something like: Do I have to declare my own login view if I want to include the login form everywhere in my web page?
Thank you.
Found answer:
Change settings.LOGIN_REDIRECT_URL in your settings.py,
below code is copy from django:
if request.method == "POST":
form = authentication_form(data=request.POST)
if form.is_valid():
# Ensure the user-originating redirection url is safe.
if not is_safe_url(url=redirect_to, host=request.get_host()):
redirect_to = settings.LOGIN_REDIRECT_URL
...
The below allows redirects the user to the page they were attempting to access after they log in, but without having to write a custom view. It contains all the code you need to add to make it work. (As an aside, not all the TEMPLATE_CONTEXT_PROCESSORS are needed, but if you set a value to it explicitly you overwrite the defaults so need to re-add them.)
settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.request",
"django.core.context_processors.static",
)
urls.py
from django.contrib.auth.views import login, logout
...the other imports for your app ...
urlpatterns = patterns('',
(r'^login/$', login, {'template_name':'login.html'} ),
(r'^logout/$', logout,{'template_name':'logout.html'}),
...the other urls for your app...
)
login.html
<html>
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
{{form}}<br/>
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
</html>
logout.html
<html>
<p>You are logged out. To log in again, click here.</p>
</html>
views.py
#login_required(login_url="/login/")
def view1(request):
....this is a view you want to protect with security...
#login_required(login_url="/login/")
def view1(request):
....this is a view you want to protect with security...
I used something like this with default login view:
{% if form.errors %}
<p class="error">Sorry, that's not a valid username or password</p>
{% endif %}
<form action="{% url login %}" method="post">
{% csrf_token%}
<label for="username">User name:</label>
<input type="text" name="username" value="" id="username">
<label for="password">Password:</label>
<input type="password" name="password" value="" id="password">
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ request.get_full_path }}" />
</form>
# or if it's not declareв шт urls:
<form action="{% url django.contrib.auth.views.login %}?next={{ request.get_full_path }}" method="post">
everything worked fine.
PS: are you absolutely sure that "context_processors.request" is included in settings? Forgetting to include it is a common problem.
UPD: As far as I know, there are no way to make default login view to redirect on failed login (It just doesn't work that way).
Still i may be wrong
Finally I created a login view that calls django.contrib.auth.views.login internally.
I'd suggest to pass a previous url as a parameter within the url:
/accounts/login/?next=my_previous_url
and then use this value in a view
request.next
{{request.get_full_path}} gives you the current path, so is normal that the redirect points to the same place, change it for {{next}} in your registration/login.html template
Adding up to #Sean's anwer. Code for iterating over each form field in order to write field error above the miss-typed field.
So, in Sean's login.html is the existing code:
login.html
<html>
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
{{form}}<br/> <!-- I can change! -->
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
</html>
Now what you should do is replace the "I can change!" line (4th line in the above code snippet) with following code:
{% for field in form %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<span class="text-danger small"> {{ field.errors }}</span>
</div>
<label class="control-label col-sm-2">{{ field.label_tag }}</label>
<div class="col-sm-10"> {{ field }}</div>
</div>
{% endfor %}
You can use this snippet for other forms too (for example registration). :)
I stumble upon this question in my process of implementing Facebook Account Linking. The problem is the same: how do I correctly redirect django after successful login?
Remember this: your settings.py contain LOGIN_REDIRECT_URL right? So, that's the only place where you should do the logic of redirecting. To do that, first connect this signal (put this in your views.py):
def after_success_login(sender, user, request, **kwargs):
alt = request.GET.get('account_linking_token')
if alt is not None:
uri = request.GET.get('redirect_uri')
request.session['fb_redirect_uri'] = uri
user_logged_in.connect(after_success_login)
The logic above may not reflect your case, but the idea is setting up a session variable to be read in the route defined as LOGIN_REDIRECT_URL.
So, in my case:
def index(request):
if not request.user.is_authenticated():
form = SignUpForm()
return render(request, 'index.html', {'form': form})
else:
# FB ACCOUNT LINKING!
if 'fb_redirect_uri' in request.session:
redirect_uri = request.session['fb_redirect_uri']
del request.session['fb_redirect_uri']
to = '{}&authorization_code={}'.format(redirect_uri, request.user.username)
print('to', to)
return redirect(to)
That's it!
Add a decorator before the view function should be OK.
#login_required
see here for details

Categories

Resources