Render_to_string and response.content.decode() not matching - python

I'm writing my first Django app by following along with this book:
http://chimera.labs.oreilly.com/books/1234000000754/ch05.html#_passing_python_variables_to_be_rendered_in_the_template
In the book there is a test that is verifying that the html is being returned as it is supposed to. Here is the test:
def test_home_page_returns_correct_html(self):
request = HttpRequest()
response = home_page(request)
expected_html = render_to_string('home.html')
print(expected_html)
print(response.content.decode())
self.assertEqual(response.content.decode(), expected_html)
My test is failing on the assertEqual test because I have added a csrf token in my HTML using the Django Template Language. Here is what my HTML page looks like:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>To-Do lists</title>
</head>
<body>
<h1>Your To-Do list</h1>
<form method="POST">
<input name="item_text" id="id_new_item" placeholder="Enter a to-do item"/>
{% csrf_token %}
</form>
<table id="id_list_table">
<tr><td>{{ new_item_list }}</td></tr>
</table>
</body>
</html>
My assert is failing due to the render_to_string method not including the token. Here is what my two print statements included in my test print out:
F<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>To-Do lists</title>
</head>
<body>
<h1>Your To-Do list</h1>
<form method="POST">
<input name="item_text" id="id_new_item" placeholder="Enter a to-do item"/>
</form>
<table id="id_list_table">
<tr><td></td></tr>
</table>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>To-Do lists</title>
</head>
<body>
<h1>Your To-Do list</h1>
<form method="POST">
<input name="item_text" id="id_new_item" placeholder="Enter a to-do item"/>
<input type='hidden' name='csrfmiddlewaretoken' value='VAiGvXZLHCjxWEWdjhgQRBwBSnMVoIWR' />
</form>
<table id="id_list_table">
<tr><td></td></tr>
</table>
</body>
</html>
F.
He doesn't have this problem in the book (he's using 1.8), so I was wondering if the method behavior has changed, or how I would write this test to pass.

The request argument was added to render_to_string in Django 1.8. You could try changing the line in your test to:
expected_html = render_to_string('home.html', request=request)
It's only required to make this change in Django 1.9+, the test passes without the request in Django 1.8.

I found this solution which has worked for the latest Django version - 3.0.6
#add a function to the post request test function
def remove_csrf_tag(text):
"""Remove csrf tag from TEXT"""
return re.sub(r'<[^>]*csrfmiddlewaretoken[^>]*>', '', text)
...
# then change assertion
def test_home_page_can_save_a_POST_request(self):
...
self.assertEqual(
remove_csrf_tag(response.content),
remove_csrf_tag(expected_html)
)

Related

render template not changing the url of the page

I'm creating a login, register account website and it works well except for when i use the render template command as it loads the page successfully but then the url of the page is the URL of the previous page and when I refresh it, it goes back to the previous page. But when I use the redirect(url_for command I'm not able to pass variables through it
class LoginForm(FlaskForm):
username= StringField(validators=[InputRequired(),Length(min=4,max=20)],render_kw={"placeholder":"Username"})
password= PasswordField(validators=[InputRequired(),Length(min=8,max=20)],render_kw={"placeholder":"Password"})
submit =SubmitField("Login")
#app.route("/login",methods= ['GET','POST'])
def login():
form=LoginForm()
print("in login html")
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user:
if user.password==form.password.data:
login_user(user)
print("password entered")
userUsername=form.username.data
#return redirect(url_for('dashboard',userUser=userUsername,))
return render_template("dashboard.html",userUser=userUsername)
else:
print("incorrect pass")
return render_template("login.html",form=form)
#app.route("/dashboard",methods= ['GET','POST'])
#login_required
def dashboard():
return render_template("dashboard.html")
this is my python code, my dashboard.html-
<!DOCTYPE html>
<html lang="en">
<head>
<title>Dashboard</title>
<link href = "../static/main.css" rel = "stylesheet">
</head>
<div class = "container1">
<body>
<div data-role="page" id="welcome" data-url="{{ url_for('dashboard') }}">
{%extends "layout.html"%}
{%block content%}
<p>{{userUser}}</p>
<h1>Hello you are logged in. </h1>
<div class = "container1">Welcome {{userUser}}!</div>
<form action ="{{url_for('login')}}" method = POST>
<br> <button type = "submit">Logout</button>
</form>
</div>
{%endblock%}
</body>
</html>
now my login.html
<!DOCTYPE html>
<html lang = "en">
<head>
<link href = "../static/main.css" rel = "stylesheet">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
</head>
<body>
<div class = "container">
<h1>Login</h1>
<form method="POST" actions="">
{{ form.hidden_tag() }}
{{ form.username }}
{{ form.password }}
{{ form.submit }}
</form>
Don't have an account? Sign Up
<div class = "message"> {{errorMessage | safe}}</div>
</div>
</body>
</html>
when i pass userUser through redirect(url_for it doesnt work but with render templates it does but the url remains /login

Page not found (404) Request Method: POST

I've been stuck with this error for a few days and am not able to find where the mistake is.
views section
from django.shortcuts import render,redirect
from django.contrib.auth.models import User,auth
# Create your views here.
def register(request):
if(request.method=='POST'):
username=request.POST['username']
password1=request.POST['password1']
password2=request.POST['password2']
user=User.objects.create_user(username=username,password=password1)
user.save();
print("user created")
return redirect('/')
else:
return render(request,'registration.html')
urls.py section
from django.urls import path
from . import views
urlpatterns=[path('register/',views.register,name="register")]
html section
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Registration</title>
</head>
<body>
<form action="register" method="POST">
{% csrf_token %}
USERNAME <input type="text" name="username" id=""> <br><br>
PASSWORD <input type="password" name="password1" id=""> <br><br>
RETYPE PASSWORD <input type="password" name="password2" id=""> <br><br>
<input type="submit" value="">
</form>
</body>
</html>
Issue
create_user() doesn't require save() method to be called for creating instance.
You have given only action="register" which is not valid at all, you need to give url tag, to perfectly make route. That's the case for page not found which is the main question.
So, with some modifications try below code:
Views.py
def register(request):
if(request.method=='POST'):
username=request.POST['username']
password1=request.POST['password1']
password2=request.POST['password2']
user=User.objects.create_user(username=username,password=password1)
print("user created")
return redirect('/')
else:
return render(request,'registration.html')
Template file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Registration</title>
</head>
<body>
<form action="{% url 'register' %}" method="POST">
{% csrf_token %}
USERNAME <input type="text" name="username" id=""> <br><br>
PASSWORD <input type="password" name="password1" id=""> <br><br>
RETYPE PASSWORD <input type="password" name="password2" id=""> <br><br>
<input type="submit" value="Save">
</form>
</body>
</html>

I don't understand the reason for this error "jinja2.exceptions.TemplateRuntimeError: extended multiple times"

My layout.html file is:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" //Since this is the base template, this will be rendered. This can stay the same for all the html pages
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head><!--creating blocks. Blocks allows our template to change some specific functionality of the base template-->
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<meta name="generator" content="Geany 1.37.1" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.1/dist/css/bootstrap.min.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-uWxY/CJNBR+1zjPWmfnSnVxwRheevXITnMqoEIeG1LJrdI0GlVs/9cVSyPYXdcSF" crossorigin="anonymous">
{% block head %}
<title>AM Calibration</title>
{% endblock %}
</head>
<body> <!--By creating the block, the content below is going to be shown in every page-->
{% include 'includes/_navbar.html' %}
<div class="container">
{% block body %}
{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-/bQdsTh/da6pkI1MST/rWKFNjaCP5gBSY4sEBT38Q/9RBh9AH40zEOg7Hlq2THRZ" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.1/dist/js/bootstrap.min.js">
</body>
</html>
I have extended the layout.html file in several different templates and not errors but when I {% entends "layout.html"%} in the register.html file, I get the "jinja2.exceptions.TemplateRuntimeError: extended multiple times"
I have used WTForms to create the registration form.
class RegisterForm(Form):
name = StringField('Name', [validators.Length(min=1, max=50)])
username = StringField('Username', [validators.Length(min=4, max=25)])
email = StringField('Email', [validators.Length(min=6, max=50)])
password = PasswordField('Password', [
validators.DataRequired(),
validators.EqualTo('confirm', message='Passwords do not match')
])
confirm = PasswordField('Confirm Password')
Routing the registration form to the Home page
#app.route("/register", methods=['GET','POST'])
def register():
form = RegisterForm(request.form) #the request function help to bring in the form.
if request.method == 'POST' and form.validate():
return render_template("register.html")
return render_template("register.html", form=form)
if __name__== "__main__":
##debug=True is a keyword argument that will allows not to rerun the server everytime we make a change. and automatically update the website.
app.run(debug=True)
My register.html file is in the templates folder. This is the Registration form and the _formhelper.html is used here to improve the appearence of the form. It looks like below:
{% extends "layout.html" %}
{% block body %}
<h1>Register</h1>
{% from "includes/_formhelpers.html" import render_field %}
<form method="POST" action="">
<div class="form-group">
{{render_field(form.name, class_="form-control")}}
</div>
<div class="form-group">
{{render_field(form.email, class_="form-control")}}
</div>
<div class="form-group">
{{render_field(form.username, class_="form-control")}}
</div>
<div class="form-group">
{{render_field(form.password, class_="form-control")}}
</div>
<div class="form-group">
{{render_field(form.confirm, class_="form-control")}}
</div>
<p><input type="submit" class="btn btn-primary" value="Submit"></p>
</form>
{% endblock %}

Django Form request method is always GET, even method=“POST” is mentioned in HTML template

Please help me with this query. I have a project to work on...
Thanks!!
HTML file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>File Upload</title>
</head>
<body>
<form method="post" class="post-form" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
</body>
</html>
Views.py file
You should add "action" attribute to your form so that the server can choose where to post the data. Add this attribute to your form tag:
action="{% url 'url_name' %}"
url_name here is the name in urlpatterns to receive a request from the form
For example:
urlpatterns = [
path('url_name/', views.index, name = 'url_name'),
]
Views:
def index(request):
if request.method == 'POST':
print("Valid")
student = Studentform(request.POST, request.FILES)
if student.is_valid():
handle_uploaded_file(request.FILES['file'])
student.save() # <== HERE
return HttpResponse('File uploaded successfully')
else:
return HttpResponse('Not uploaded')
else:
student = Studentform()
return render(request, 'name_of_your_upload.html', {'student': student})
Template:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>File Upload</title>
</head>
<body>
<form method="post" class="post-form" enctype="multipart/form-data">
{% csrf_token %}
{{ student.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
</body>
</html>

How do I reset my HTML form with Python Flask?

I'm building a very simple web app (using Python Flask) that will display some images to the user and get some response from the user.
Below is my code (ignoring the other parts of my code not related to this question):
#app.route('/gallery',methods=['GET','POST'])
def get_gallery():
image_names = os.listdir(r'C:\Users\xxxvn\images')
b=[str(i)+"|"+str(request.form.get(i)) for i in image_names]
print (b)
return render_template("gallery.html", image_names=image_names)
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"
integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">Gallery</h1>
</div>
{{image_names}}
<hr>
<form method="POST">
{% for image_name in image_names %}
<div class="col-lg-3 col-md-4 col-xs-6 thumb">
<img class="img-responsive" src=" {{url_for('send_image', filename=image_name)}}">
<input type="radio" name={{image_name}} id="radio1" value="YES" />YES<br>
<input type="radio" name={{image_name}} id="radio2" value="NO"/>NO<br>
<hr>
</div>
{% endfor %}
<input type="submit" name="submit_button" value="SUBMIT"/>
</form>
</div>
</div>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"
integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS"
crossorigin="anonymous"></script>
</body>
</html>
My current app
Question:
I want to reset the form as soon as the user hits submit and capture the output in list "B". The form should not be resubmitted with old values if the user hits refresh.
Do a request method check and put in your capture login in it.
from flask import request
#app.route('/gallery',methods=['GET','POST'])
def get_gallery():
image_names = os.listdir(r'C:\Users\xxxvn\images')
if request.method == "POST":
b=[str(i)+"|"+str(request.form.get(i)) for i in image_names]
print (b)
return render_template("gallery.html", image_names=image_names)

Categories