I'm reading the Djangobook and I'm on ch 7.There is actually a line that says "#todo - explain CSRF token"
When I was following the examples (I'm pretty sure I've followed them exactly), I cannot get the code to function properly.
Here is my template
<html>
<head>
<title>Contact us</title>
</head>
<body>
<h1>Contact us</h1>
{% if errors %}
<ul>
{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<form action="/contact/" method="post">
{% csrf_token %}
<p>Subject: <input type="text" name="subject"></p>
<p>Your e-mail (optional): <input type="text" name="email"></p>
<p>Message: <textarea name="message" rows="10" cols="50"></textarea></p>
<input type="submit" value="Submit">
</form>
</body>
</html>
Here is my view
from django.core.mail import send_mail
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.template import RequestContext
def contact(request):
errors = []
if request.method == 'POST':
if not request.POST.get('subject', ''):
errors.append('Enter a subject.')
if not request.POST.get('message', ''):
errors.append('Enter a message.')
if request.POST.get('email') and '#' not in request.POST['email']:
errors.append('Enter a valid e-mail address.')
if not errors:
send_mail(
request.POST['subject'],
request.POST['message'],
request.POST.get('email', 'noreply#example.com'),
['siteowner#example.com'],
)
return HttpResponseRedirect('/contact/thanks/')
return render(request, 'contact_form.html',
{'errors': errors}, context_instance=RequestContext(request))
This is the error I'm getting
Forbidden (403)
CSRF verification failed. Request aborted.
Help
Reason given for failure:
CSRF token missing or incorrect.
In general, this can occur when there is a genuine Cross Site Request Forgery, or when Django's CSRF mechanism has not been used correctly. For POST forms, you need to ensure:
- Your browser is accepting cookies.
- The view function uses RequestContext for the template, instead of Context.
- In the template, there is a {% csrf_token %} template tag inside each POST form that targets an internal URL.
- If you are not using CsrfViewMiddleware, then you must use csrf_protect on any views that use the csrf_token template tag, as well as those that accept the POST data.
You're seeing the help section of this page because you have DEBUG = True in your Django settings file. Change that to False, and only the initial error message will be displayed.
You can customize this page using the CSRF_FAILURE_VIEW setting.
EDIT******
I discovered that I can view the source code for my form and the csrf_token isn't being inserted even though I have it in my template. I looked up common solutions. Some people suggested I do this
return render_to_response('contact_form.html',
{'errors': errors}, context_instance=RequestContext(request))
But this doesn't work for me either.
I just checked my settings.py and I see 2 middlewares added not just CsrfViewMiddleware -
MIDDLEWARE_CLASSES = (
...
‘django.middleware.csrf.CsrfViewMiddleware’,
‘django.middleware.csrf.CsrfResponseMiddleware’,
)
Try adding more.
Related
Running Django 4.1.1. Having this code below in template. By clicking a button it sends a data to delete apropriate marker.
<form method="POST">
{% csrf_token %}
<ol>
{% for marker in markers %}
<li>
{{ marker }} - <button class="button btn-primary" id="delete" value="{{ marker.pk }}" type="submit">Delete</button>
</li>
{% endfor %}
</ol>
</form>
In views.py
def user_markers(request):
markers = Marker.objects.filter(owner_id=request.user.id).select_related()
if request.method == "POST":
print(request.POST.get("delete")) # gives me None
marker = Marker.objects.get(pk=request.POST.get("delete"))
marker.delete()
context = {
"markers": markers,
}
return render(request, "hub/markers.html", context)
The problem is that request.POST.get("delete") is empty.
POST data has only 'csrfmiddlewaretoken'
Do I miss something?
You need to specify this as name, so:
<button class="button btn-primary" id="delete" name="delete" value="{{ marker.pk }}" type="submit">Delete</button>
If you do not specify a name, it will not be include in the POST data, and thus it will not contain any value that is passed with the button.
You might want to improve the view function:
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect
#login_required
def user_markers(request):
markers = Marker.objects.filter(owner_id=request.user.id).select_related()
if request.method == 'POST':
get_object_or_404(
Marker, owner=request.user, pk=request.POST.get('delete')
).delete()
return redirect('name-of-some-view') # need to redirect
context = {
'markers': markers,
}
return render(request, 'hub/markers.html', context)
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
Note: It is often better to use get_object_or_404(…) [Django-doc],
then to use .get(…) [Django-doc] directly. In case the object does not exists,
for example because the user altered the URL themselves, the get_object_or_404(…) will result in returning a HTTP 404 Not Found response, whereas using
.get(…) will result in a HTTP 500 Server Error.
i have a server deployed in AWS using Django and every thing working fine until i tap on login with facebook Button it shows the normal facebook login popup and after typing my email and password instead of going to the next page it gave me CSRF verification failed. Request aborted.
as you can see i've {% csrf_token %} in the code for showing login with facebook button using js_sdk:
{% extends 'restaurant/base_auth.html' %}
{% load bootstrap4 %}
{% block title %}Akalat-Shop{% endblock %}
{% block heading %}Akalat-Shop - Sign In{% endblock %}
{% block content %}
{% load socialaccount %}
{% providers_media_js %}
Login with Facebook
<form action="" method="post">
{% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="btn btn-primary btn-block">Sign In</button>
</form>
<div class="text-center mt-3">
Become a Restaurant
</div>
{% endblock %}
also i tried those in settings.py :
LOGIN_REDIRECT_URL = '/'
ACCOUNT_DEFAULT_HTTP_PROTOCOL = 'https'
SOCIAL_AUTH_REDIRECT_IS_HTTPS = True
//all configurations of facebook login
my views.py i've checked also for using #csrf_exempt:
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
#login_required(login_url="/restaurant/sign_in/")
def restaurant_home(request):
return redirect(restaurant_order)
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
#login_required(login_url="/restaurant/sign_in/")
def restaurant_order(request):
if request.method == "POST":
order = Order.objects.get(id=request.POST["id"])
if order.status == Order.COOKING:
order.status = Order.READY
order.save()
orders = Order.objects.filter(restaurant = request.user.restaurant).order_by("-id")
return render(request, 'restaurant/order.html', {"orders": orders})
my configurations in facebook dashboard for callback url in the screenshot below:
i don't know where is the problem but may be from using js_sdk in facebook login caused this block and thanks in advance for helping ✨🤝
#Contact.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class='row'>
<div class ='col-md-4 col-md-offset-4'>
<h1> {{title}} </h1>
{% if confirm_message %}
<p>{{ confirm_message }}</p>
{% endif %}
{% if form %}
<form method='POST' action=''>
{% csrf_token %}
{{ form.errors }}
{{ form.non_field_errors }}
{% crispy form %}
<input type='submit' value='submit form' class='btn btn-default' />
</form>
{% endif %}
</div>
</div>
{% endblock %}
# froms.py
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Layout, Field
from crispy_forms.bootstrap import (PrependedText, PrependedAppendedText, FormActions)
class contactForm(forms.Form):
name = forms.CharField(required = False , max_length =100, help_text="100 characters max ")
email= forms.EmailField(required = True)
comment = forms.CharField(required =True, widget=forms.Textarea)
Server Logs
System check identified no issues (0 silenced).
September 13, 2017 - 07:38:19
Django version 1.11.5, using settings 'app3.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
GET
hello from not valid
[13/Sep/2017 07:38:23] "GET /contact/ HTTP/1.1" 200 5413
[13/Sep/2017 07:42:20] "GET / HTTP/1.1" 200 4356
[13/Sep/2017 07:42:27] "GET /about/ HTTP/1.1" 200 3985
GET
hello from not valid
[13/Sep/2017 07:42:37] "GET /contact/ HTTP/1.1" 200 5413
The request never becomes post. When I hit submit on the form it
never shows up as post request. What could possibly I be doing wrong ?
#Views page
from django.shortcuts import render
from .forms import contactForm
from django.conf import settings
from django.core.mail import send_mail
def contact(request):
form = contactForm()
confirm_message = None
title = 'Contact'
context ={'title' : title, 'form' : form }
print request.method
# print form
# if request.method=='GET':
# form = contactForm()
if request.method =='POST':
form = contactForm(request.POST )
print "hello from not valid "
if form.is_valid():
print "hello"
name = form.cleaned_data['name']
comment=form.cleaned_data['comment']
emailFrom=form.cleaned_data['email']
subject ='Message from mysite.com'
message='%s %s' %(comment, name )
emailTo=[settings.EMAIL_HOST_USER]
title = 'Thank you'
confirm_message="Thank you, we ll get back to you "
context ={'title' : title,'confirm_message' :
confirm_message}
template ='contact.html'
return render(request , template , context)
This is my views page handling all the business logic for the application
When I run this application, the code never reaches the request==post block. I am unable to figure out why ? I pasted contact.html and forms.py for more visibility.
EDIT:
I have implemented all the changes suggested but the form never renders the post method. I could say something wrong with form but I don't know what.
UPDATE2:
The issue has been resolved and the problem seems to crispy forms. I read the documentation and couldn't find anything to pin point the error besides the fact that it was rendering the request as post. Decided to remove it and now it works perfectly fine.
Thank you all for your help and suggestions.
You can see "hello from not valid" string in your server log that means your POST request is successfully sended to server.
However, second if statement checks if form is valid and this is the line where things get south. Since you do not have else case for not valid form, you cannot see the right error message.
Fix your form and cover the not valid case.
In your template your form is constructed wrongly.
If you use {% crispy %} tag in your template, it makes a form.
If you don't want form tags included, set the form_tag attribute for your form helper to False.
self.helper.form_tag = False
You need not explicitly use {% csrftoken %} tag, crispy adds that for you.
Also I don't see that you're using crispy form helper in your forms.py.
new to Django and I'm having a little trouble with this.
I have a registration form that creates rows in a database on successful submission.
Upon submission, I would like to redirect the user to a login form (which requires me to pass in the form as a parameter for render()), passing a status of 'REGISTRATION_SUCCESSFUL' as a parameter as well.
return render(request, 'login.html', {
'form': login_form(),
'status': 'REGISTRATION_SUCCESSFUL'
})
Based on the status, my template then displays a message to indicate the successful registration.
All works fine until you try to refresh the login page, it tries to submit the registration form again (I suppose because the request was forwarded to the login page).
Everything I've tried involves forwarding the request, which is proving to be a problem. Help?
Thanks in advance!
After form submission instead of using render, use HTTPResponseRedirect. Always return an HttpResponseRedirect after successfully dealing with POST data. This prevents data from being posted twice if a user hits the Back button.
The problem that you face here is that HTTPResponseRedirect does not allow you to pass the context of the status being REGISTRATION_SUCCESSFUL.
Based on the status, my template then displays a message to indicate the successful registration.
This is where you could use Django's messages framework. Right before the HTTPResponseRedirect, you could do something like:
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import RegistrationForm
def registration(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
# ... do something special here if needed
messages.add_message(request, messages.INFO, 'Registration was successful!')
return HttpResponseRedirect(reverse('login_page'))
else:
form = RegistrationForm()
return render(request, 'registration.html', {'form': form})
Now in your "login_page", you can show the messages. FYI - the messages are on a per-session basis, so the "Registration was successful" message will only be displayed to the user you redirected:
login_page.html: (example from the messages documentation)
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }} </li>
{% endfor %}
</ul>
{% endif %}
<form method="post">{% csrf_token %}
{{ form }} {# Your login form #}
</form>
In this example, any messages you added will be displayed above your login form. If there are other messages such as "invalid registration," they will be displayed.
Please be sure to Enable the Django Messages Framework by adding the following to your settings.INSTALLED_APPS:
INSTALLED_APPS = (
...
'django.contrib.messages',
)
And your settings.MIDDLEWARE_CLASSES must contain:
MIDDLEWARE_CLASSES = (
...
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
And your settings.TEMPLATE_CONTEXT_PROCESSORS must contain:
TEMPLATE_CONTEXT_PROCESSORS = (
...
'django.contrib.messages.context_processors.messages',
)
After registration is successful, you might have to consider redirect which is a common practice after a successful post is done.
Django provides a short-cut for redirects.
return redirect('some-view-name', foo='bar')
return redirect('/some/url/')
For your case you can consider:
kwargs={'status': 'REGISTRATION_SUCCESSFUL' }
return redirect('login-view', **kwargs)
from django.contrib import messages
from django.http import HttpResponseRedirect
if form1.is_valid():
registration = form1.save(commit=False)
registration.save()
messages.success(request, 'Your message here.')
return HttpResponseRedirect('/login/')
in templates.
in div where you want to display the message
{% block message %}
{% include "includes/messages.html"%}
{% endblock %}
Then create the message.html file in include folder
<div id="messages">
{% for message in messages %}
<div class="alert alert-{{message.tags }} alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
{{ message }}
</div>
{% endfor %}
</div>
Done so your page will redirect to login page and on that page message will get display.
i am trying to do a login in django but i get this error, i check the CSRF documentation and nothing works for me.
Here is the HTML:
<body>
<section class="container">
<div class="login">
<h1>Login to Web App</h1>
{% if form.errors %}
<p class="error">Lo sentimos, la combinacion de usuario y contrasena no es correcta!</p>
{% endif %}
<form action="/accounts/auth/" method="post">
{% csrf_token %}
<input type='hidden' name='csrfmiddlewaretoken' value='randomchars'/>
<p><input name="username" type="text" name="login" value="" placeholder="Username"></p>
<p><input name="password" type="password" name="password" value="" placeholder="Password"></p>
<p class="submit"><input type="submit" name="commit" value="Login"></p>
</form>
</div>
</body>
Like you see above i use the {% csrf_token %} and i have 'django.middleware.csrf.CsrfViewMiddleware' in my installed apps.
And my views are:
from django.http import HttpResponse,HttpResponseRedirect
from django.template.loader import get_template
from django.template import Context
from datetime import datetime
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.contrib import auth
from django.core.context_processors import csrf
from models import *
from django.shortcuts import get_object_or_404
from forms import *
from django.template.context import RequestContext
from django.contrib.auth.decorators import login_required
from django.contrib.auth import authenticate, login
def login(request):
c = {}
c.update(csrf(request))
return render_to_response('login.html', c)
def auth_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
auth.login(request.user)
return HttpResponse('/accounts/loggedin')
else:
return HttpResponse('/accounts/invalid')
i redirect to an other HTML file where i dont use the {% csrf_token %}.
Theory
A couple of things are required to make the csrf protection work (check out the docs):
Your browser has to accept cookies from your server
Make sure you have 'django.middleware.csrf.CsrfViewMiddleware' included as middleware in your settings.py (alternatively use the decorator csrf_protect() on particular views you want to protect)
Make sure you pass on the csrf token from django.core.context_processors.csrf to the context manager.
When you load your page, have a look in the page source using your favorite browser. Don't open the template html file, open the url which point to the view containing the form. Look at where you placed the {% csrf_token %}. If you see something like
<input type='hidden' name='csrfmiddlewaretoken' value="jdwjwjefjwdjqwølksqøwkop2j3ofje" />
you should be ok.
If you on the other hand see NOTPROVIDED, something has gone wrong while creating the csrf token. By looking in the source code (context_processors.py and csrf.py), we can find out what:
csrf(request) returns {'csrf_token': 'NOTPROVIDED'} if get_token(request) returns None.
get_token(request) returns request.META.get("CSRF_COOKIE", None).
I assume this means that it would return None if the cookie isn't successfully created.
Fix
For you, this means that you should first replace
<form action="/accounts/auth/" method="post" {% csrf_token %}>
with
<form action="/accounts/auth/" method="post">
{% csrf_token %}
(...)
</form>
We'd like the csrf field to be inside <form>...</form>, not inside <form>. As the code is at the moment, it will be converted to
<form action="/accounts/auth/" method="post" <input type='hidden' name='csrfmiddlewaretoken' value='randomchars' />>
and we would rather like
<form action="/accounts/auth/" method="post">
<input type='hidden' name='csrfmiddlewaretoken' value='randomchars' />
After that - have a look at the source code, and see if you can find the csrf field. If you can see it, everything should work in theory.
You can also check that the csrf cookie has been set in your browser, e.g. in Chrome, right-click the web page, and select Insepect Element. Select the Resources tab, and click on cookies. You should find a cookie name csrftoken there.
If you still have problems, double-check the middleware tuple in your settings.py and double-check that your browser accept cookier from your server as described above.
Clear your browser cache and try again. Maybe it is using the CSRF token saved in cached cookie.
With Addition of above answer, Try Adding following lines in the views
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def somathing():
return something