Getting Django CSRF error with raw html form - html

I'm trying to set up a raw html form where a user can make a suggestion and then save it on a database with a POST method, but I keep getting a Forbidden (403) CSRF verification failed. Request aborted. even after following the steps in the Help section.
I have found that I don't get the error if I add csrf_exempt on top of my view like this:
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def suggest_ptags(request):
context = {}
print("Form is submitted.")
return render(request, "partials/search_form.html", context)
But I was made aware that It removes completly the CSRF protection and I don't want that.
So what should I do?
Here's my search_form.html form in a partials folder in templates:
<!-- Suggestion Form in popup -->
<div class="prop-modal">
<div class="prop-content">
<a class="btn-close-prop">×</a>
<img src="{% static 'images/pyramids.svg' %}">
<form action="/suggest_ptags/" class="feedback-form" method="POST" enctype="text/plain">
{% csrf_token %}
<h5 class="title-prop">Suggestion</h5>
<input class="input-prop" name="suggest" rows="3" cols="37" placeholder="suggest something..."></input>
<input class="button-prop" type="submit" value="Envoyez"></input>
</form>
</div>
</div>
My current Views.py:
from django.views.decorators.csrf import ensure_csrf_cookie
#ensure_csrf_cookie
def suggest_ptags(request):
context = {}
print("Form is submitted.")
return render(request, "partials/search_form.html", context)
And in my Urls:
from django.conf.urls import url
from django.contrib import admin
from search.views import HomeView, ProductView, FacetedSearchView, autocomplete, suggest_ptags
from .settings import MEDIA_ROOT, MEDIA_URL
from django.conf.urls.static import static
urlpatterns = [
url(r'^$', HomeView.as_view(), name='home'),
url(r'^admin/', admin.site.urls),
url(r'^suggest_ptags/$', suggest_ptags, name='suggest_ptags'), #Suggestions
url(r'^product/(?P<slug>[\w-]+)/$', ProductView.as_view(), name='product'),
url(r'^search/autocomplete/$', autocomplete),
url(r'^search/', FacetedSearchView.as_view(), name='haystack_search'),
] + static(MEDIA_URL, document_root=MEDIA_ROOT)
Any solutions?

You shouldn't use enctype="text/plain". You can remove it (which is the same as enctype="multipart/form-data"), or use enctype="multipart/form-data" if you are uploading files.

Related

Django reverse for 'password_change_done' not found

I have a small application that I'm writing, but I have came into some trouble with the password changing elements used with the Django authentication framework. But I always get the error after changing a password that: Reverse for 'password_change_done' not found. 'password_change_done' is not a valid view function or pattern name. Here is my code below:
#account urls.py
from django.urls import path, include
from django.contrib.auth import views as auth_views
from . import views
app_name = 'account'
urlpatterns = [
#Password Changes URLs
path('password_change/', auth_views.PasswordChangeView.as_view(), name='password_change'),
path('password_change/done/', auth_views.PasswordChangeDoneView.as_view(), name='password_change_done'),
]
This is my directory structure for the login system:
Directory Structure
Here's the password_change.html file:
<h3 style="text-align: center;">Change your password</h3>
<div class="login-form" style="text-align: center; ">
<form method="post">
{{ form.as_p }}
<p><input type="submit" value="Change"></p>
{% csrf_token %}
</form>
</div>
Any help would be greatly appreciated!
Since you use an app_name, you will need to include the namespace in the success_url of the PasswordChangeView:
# account urls.py
from django.urls import path, include
from django.contrib.auth import views as auth_views
from . import views
from django.urls import reverse_lazy
app_name = 'account'
urlpatterns = [
#Password Changes URLs
path('password_change/', auth_views.PasswordChangeView.as_view(
success_url=reverse_lazy('account:password_change_done')
), name='password_change'),
path('password_change/done/', auth_views.PasswordChangeDoneView.as_view(), name='password_change_done'),
]
some other class-based views of the auth package have success_urls, these thus should be updated as well.

How can I create redirect button with post method in Django template

I'm trying to create a button in the Django template which will redirect to another URL. But getting error 404 since Django can't recognize URL path rescribed in the urls.py.
HTML part
<form method="post" action='sts'>
{% csrf_token %}
<button class="btn btn-outline-success my-2 my-sm-0" type="submit" name="cts_link">cts</button>
</form>
urls.py
from django.conf.urls import include, url
from django.contrib import admin
from rtRegRes.views import units
from rtRegRes.views import spartan
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^units/$', units),
url(r'^units/sts/?$', spartan),
]
views.py
from django.shortcuts import render, redirect, reverse, render_to_response
from .models import rt_reg_res
from django.http import HttpResponse, JsonResponse
def units(request):
"""Return main webpage"""
return render_to_response('runtime.html')
def spartan(request):
"""Link to the other unit webpages"""
table = rt_reg_res.objects.all()
if request.method == 'POST':
qatables = request.POST.get("cts_link")
if qatables:
return render(request, 'cts.html', {'table': table})
Clicking the button following error message appears:
enter image description here
Could somebody point me what is wrong in my code
Thanks
from django.conf.urls import include, url
from django.contrib import admin
from rtRegRes.views import spartan , units
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^units/$', units),
url(r'^units/sts/?$', spartan, name='sts'),
]
and
<form method="post" action='sts'>
{% csrf_token %}
<button class="btn btn-outline-success my-2 my-sm-0" type="submit" name="cts_link">cts</button>
</form>
Swapping the order of urls should help.
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^units/sts/?$', spartan),
url(r'^units/$', units),
]
What a pity, I wasted my time for nothing (( My code was correct, I just forget to rerun server after changes, every time reloaded page instead and did not see real changes. Anyway, thanks for the responses guys!!

Jinja2 Exceptions - Cannot find attribute

Working on a problem in Flask/ Python. Had a few of these errors pop up and I've been able to squash them as they arise; however, this one I cannot seem to get to the bottom of.
I have a simple form which allows users to login.
But each time I load the page I am greeted with this error:
jinja2.exceptions.UndefinedError: 'shop.forms.LoginForm object' has no attribute 'submit'
Below is the code that I am working with, thanks in advance.
p.s. I have seen similar posts regarding the hidden_tag() attribute, but the fixes suggested are not working for this scenario.
routes.py
import os
from flask import render_template, url_for, request, redirect, flash
from shop import app, db
from shop.models import Author, Book, User
from shop.forms import RegistrationForm, LoginForm
from flask_login import login_user, current_user, logout_user, login_required
#app.route("/login", methods=['GET', 'POST'])
def login():
form = LoginForm()
if request.method == 'POST':
user = User.query.filter_by(email=form.email.data).first()
if user is not None and user.verify_password(form.password.data):
login_user(user)
return redirect(url_for('home'))
return render_template('login.html', title='Login', form=form)
forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, BooleanField
from wtforms.validators import DataRequired, Length, Email, EqualTo
class LoginForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
sumbit = SubmitField('Login')
login.html
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
{% extends "layout.html" %}
{% block content %}
<form method="POST" action="">
{{ form.csrf_token }}
<div class="">
{{ form.email.label }} {{ form.email}}
</div>
<div class="">
{{ form.password.label }} {{ form.password}}
</div>
<div class="">
{{ form.submit() }}
</div>
</form>
{% endblock content %}
</body>
</html>
EDIT: Removing the () from submit doesn't solve the issue. Just removes the instance of the button entirely from the template. See below:
Change form.submit() to form.submit and it will show the submit button in template.
Here is an example of using flask_wtf for a login form.
Example of using Flask wtform:
app.py:
from flask import render_template, url_for, request, redirect, flash, Flask
from forms import LoginForm
app = Flask(__name__)
app.secret_key = 'secret key'
#app.route("/login", methods=['GET', 'POST'])
def login():
form = LoginForm()
if request.method == 'POST':
user_email = form.email.data
user_password = form.password.data
if user_email and user_password:
return "{} - {}".format(user_email, user_password)
return render_template('login.html', title='Login', form=form)
if __name__ == '__main__':
app.run(debug=True)
forms.py:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, BooleanField
from wtforms.validators import DataRequired, Length, Email, EqualTo
class LoginForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
submit = SubmitField('Login')
login.html:
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<form method="POST" action="">
{{ form.csrf_token }}
<div class="">
{{ form.email.label }} {{ form.email }}
</div>
<div class="">
{{ form.password.label }} {{ form.password }}
</div>
<div class="">
{{ form.submit }}
</div>
</form>
</body>
</html>
Output:
Get request of login route:
Post request of login route:
Updates:
requirements.txt:
Click==7.0
Flask==1.0.2
Flask-WTF==0.14.2
itsdangerous==1.1.0
Jinja2==2.10
MarkupSafe==1.1.1
Werkzeug==0.15.0
WTForms==2.2.1
I successfully run this code both in my machine and in c9.io.
Get request for /login route (before submitting the form):
After submitting the form:
Issue solved!
I didn't spell submit correctly in forms.py
Simple clerical error that cost me 2 hours.

Django 1.10 - Issue passing value from Template to View

I am having an issue when it cmoes to passing variables from templates to views. Even though I am able to pass variables from view to template, I canot seem to get it right. I have looked at similar questions here.
Following the Django docs I created a forms.py script as follows:
forms.py
GNU nano 2.7.4 File: forms.py
from django import forms
class TactForm(forms.Form):
tacttime = forms.CharField(label='Tact Time', max_length=100)
Updated View
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render
from django.http import HttpResponse
from lineoee.models import Lineoee31
from .forms import TactForm
def details(request):
if request.method == 'POST':
form = TactForm(request.POST)
print(form)
else:
form = TactForm()
context = {'form' : form}
return render(request, 'linedetails/index.html',context)
Updated Template
<form method="POST" action="{% url 'details' %}">
{% csrf_token %}
{{ form.as_p }}
<label for="tacttime">Tact Time: </label>
<input id="tacttime" type="text" name="tacttime" value ="60">
<input type="submit" value="OK">
<form>
Updated URLS
from django.conf.urls import url
from django.contrib import admin
from lineoee.views import index
from lineoee.views import details
urlpatterns = [
url(r'lineoee/$', index, name='index'),
url(r'linedetails/', details, name='details'),
]
Still, no errors and no values passed to the view.
EDIT
I am now getting some data on pressing the OK button, however it is not what I was expecting. I want to be able to retrieve the text entered into the input field. How can I do this?
"POST /linedetails/ HTTP/1.1" 200 24580
<tr><th><label for="id_tacttime">Tact Time:</label></th><td><input
type="text" name="tacttime" value="60" required id="id_tacttime"
maxlength="100" /></td></tr>
Template
<div style="text-align:center;">
<form method="POST" action="{% url 'details' %}">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="adsfadsfas">
</form>
</div>
Views (EDITED)
Whatever the name you use in your input on your HTML template, that's the key you're gonna use to get what comes in the request.POST. That's why you'd like to use {{ form.field }} in the template so you know beforehand the name of the fields you're expecting to come in the request.POST
def details(request):
if request.method == 'POST':
print(request.POST)
print(request.POST.get('tacttime')
form = TactForm(request.POST)
print(form)
else:
form = TactForm()
return render(request, 'linedetails/index.html', context)
URLS
from django.conf.urls import url
from django.contrib import admin
from lineoee.views import index
from lineoee.views import details
urlpatterns = [
url(r'lineoee/$', index, name='index'),
url(r'linedetails/', details, name='details'),
]
def details(request):
if request.method == 'POST':
var = request.POST['textfield']
print(var)

Django URL mapping for different apps

Im trying to link a button in HTML to another html in my project folder for my django project. Lets say its
MyApp
-Polls
-templates
-index
-Votes
-templates
-main
-truths
-rigged
I have a a button on main that uses rigged and truths so in main it has this button
<form action="{% url 'Votes:rigged' %}">
<input type="submit" value="rigged votes" />
</form>
now i want to add another button that would link polls->index into it. Is there a way to do that without copying everything from Polls into the folder Votes?
UPDATE*
main.html
<form action="{% url 'Polls:Index' %}">
<input type="submit" value="Index" />
</form>
Polls.url
urlpatterns=[
url(r'^Index/', Index.as_view(), name="Index"),
]
Index.views
class IndexView(TemplateView):
# template location
template_name = "Polls/Index.html"
# post logic must be defined
def post(self, request, *args, **kwargs):
return redirect(reverse_lazy("Polls:Index"))
Please refer to https://docs.djangoproject.com/en/1.10/topics/http/urls/#url-namespaces
Please understand how the namespaces and named urls work. You should have a urls.py file in your 'MyApp' folder where other files like 'settings.py' and 'wigs.py' are there.
For referring to an url by namespaces you need to first register the namespace associated with url.py of your Polls app. Example from the documentation:
from django.conf.urls import include, url
urlpatterns = [
url(r'^polls/', include('polls.urls', namespace='Polls')),
]
Furthermore you must also name your url in 'polls.urls' like
url(r'^index/$', Whatever_view,name='index'),
then you can call the url as "Polls:index"