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)
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With