Django and the POST request: unexpected behaviour with a form - html

urls.py
from django.conf.urls.defaults import patterns, include, url
import myproject.views
urlpatterns = patterns('', (r'^$', myproject.views.home), (r'^login$', apolla.views.login))
views.py
import django.http
import django.template
import django.shortcuts
def home(request):
return django.http.HttpResponse("Welcome home!")
def login(request):
un = request.POST.get('username')
pa = request.POST.get('password')
di = {'unam': un, 'pass': pa}
if un and pa:
di['act'] = "/"
else:
di['act'] = "/login"
return django.shortcuts.render_to_response('login.html', di,
context_instance=django.template.RequestContext(request))
# Why does this code not send me immediately to "/" with
# username and password filled in?
login.html
<html>
<head>
</head>
<body>
<form name="input" method="post" action="{{ act }}">
{% csrf_token %}
Username:
<input type="text" name="username"><br>
Password:
<input type="password" name="password"><br>
<input id="su" type="submit" value="Submit"><br>
</form>
</body>
</html>
When I run the development server and go to localhost:8000/login and fill in a username and password and push the submit button I am not sent to localhost:8000/ as I expected from my login function in views.py, I just return to localhost:8000/login. But when I fill in any field and submit for the second time I get directed to localhost:8000.
I also used print un and print pa to see if the post caught the data from the username and password fields and it did from the first time, so why am I not being directed to localhost:8000/login from the first submit with both username and password fields filled in?

You can add redirects to your view by:
from django.http import HttpResponseRedirect
def foo_view(request):
# ...
return HttpResponseRedirect('/')

Related

Django: Template Form action not redirecting

My Form action is not redirecting to the passed view. I am calling simple_upload view method from login_form.html form action. Instead, upon clicking the login button, it stays on the same page. Below is my code:
urls.py:
from django.conf.urls import url
from django.contrib import admin
from django.conf import settings
from django.conf.urls.static import static
from uploads.core import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^', views.login_form, name='login_form'),
url(r'^upload/', views.simple_upload, name='simple_upload'),
url(r'^drop_down/$', views.drop_down, name='drop_down'),
url(r'^visualize_view/$', views.visualize_view, name='visualize_view'),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.AKASH_ROOT)
login_form.html:
{% block content %}
<button onclick="document.getElementById('id01').style.display='block'" style="width:auto;">Login</button>
<div id="id01" class="modal">
<form class="modal-content animate" action="{% url 'simple_upload' %}" method="get">
{% csrf_token %}
<div class="imgcontainer">
<span class="close" title="Close Modal">×</span>
<img src="https://www.w3schools.com/howto/img_avatar2.png" alt="Avatar" class="avatar">
</div>
<div class="container">
<label for="uname"><b>Username</b></label>
<input type="text" placeholder="Enter Username" name="uname" required>
<label for="psw"><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="psw" required>
<button type="submit">Login</button>
<label>
<input type="checkbox" checked="checked" name="remember"> Remember me
</label>
</div>
<div class="container" style="background-color:#f1f1f1">
<button type="button" class="cancelbtn">Cancel</button>
<span class="psw">Forgot password?</span>
</div>
</form>
</div>
<script>
// Get the modal
var modal = document.getElementById('id01');
// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
</script>
{% endblock %}
views.py:
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.core.files.storage import FileSystemStorage
from .models import Document
from .forms import ExpenseForm
def login_form(request):
return render(request, 'core/login_form.html')
def simple_upload(request):
return HttpResponse("Hello World")
Project hierarchy:
Your home URL pattern is not terminated, so it matches every path. It should be:
url(r'^$', views.login_form, name='login_form'),
It's not good form logic. Forms have valid and invalid actions. If your form is valid you redirect to new (success) page your user, but if not you render same (login) page. But firstly you should give a name your login page like below or use Django's inherit auth urls:
url(r'^login', views.login_form, name='login_form'),
It's my url paths:
from django.contrib import admin
from django.urls import path, include
from . import views
app_name = "user"
urlpatterns = [
path('sign_up/', views.sign_up, name="sign_up"),
path('account_activation_sent/', views.account_activation_sent, name='account_activation_sent'),
path('activate/<uidb64>/<token>/', views.activate, name="activate"),
path('login/', views.login_user, name="login"),
path('logout/', views.logout_user, name="logout"),
path('password_reset/', views.password_reset, name="password_reset"),
path('password_reset/done/', views.password_reset_done, name="password_reset_done"),
path('password_reset/<uidb64>/<token>/', views.password_reset_confirm, name="password_reset_confirm"),
path('password_reset/complete/', views.password_reset_complete, name="password_reset_complete"),
path('profile/<slug:slug>/', views.profile, name="profile"),
]
I wanna show you my simple login function and you will understand:
def login_user(request):
if request.user.is_authenticated:
return redirect("index")
else:
form = LoginForm(request.POST or None)
context = {
"form": form
}
go_to = request.POST.get('next', '/')
print(go_to)
if form.is_valid():
username = form.cleaned_data.get("username")
password = form.cleaned_data.get("password")
user = authenticate(username=username, password=password)
if user is None:
messages.error(request, "Username or password is incorrect! Try again.")
return render(request, "auths/login.html", context)
messages.success(request, "Login successful! Welcome bro.")
login(request, user)
go_to = request.POST.get('next', '/')
if go_to:
go_to = request.POST.get(
'next')
return redirect(go_to)
else:
return redirect("index")
return render(request, "auths/login.html", context)
I use Django's form and It's easy but you can use your custom form in your template. My form is like this:
class LoginForm(forms.Form):
username = forms.CharField(label="Username")
password = forms.CharField(label="Password", widget=forms.PasswordInput)
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
self.fields['username'].label = ''
self.fields['password'].label = ''
class Meta:
model = User
fields = ('username', 'password' )
I hope It will help you.

Invalid form when uploading file in Django

I need to upload file on a Django page, however, after following the official tutorial, I was not able to upload it, it always gives the error "invalid form", and when I tried to print out the error msg of the form, it says "This field is required".
One thing notable is: I have 2 forms on one page, one is this upload form and the other one is for filling out information. Not sure if this is the root cause.
I have tried all solutions provided on the Internet.
Template file:
<form id="uploadForm" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="file" value="upload" name="sourcefile">
<button type="submit">Upload</button>
</form>
Forms.py:
from django import forms
from .models import SourceFile
class UploadFileForm(forms.ModelForm):
class Meta:
model = SourceFile
fields = ('file', 'title')
Models.py:
from django.db import models
# Create your models here.
class SourceFile(models.Model):
title = models.CharField(max_length=255, blank=True)
file = models.FileField(upload_to="media/")
Views.py
def model_form_upload(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
instance = SourceFile(file_field=request.FILES['file'])
instance.save()
return JsonResponse({'error': False, 'message': 'Uploaded Successfully!'})
else:
print("Invalid form")
# return JsonResponse({'error': True, 'errors': form.errors})
else:
form = UploadFileForm()
return render(request, 'source_validation.html', {'form': form})
Your template is wrong. Either use {{ form.as_p }} which should display a file input field because file is a field in your form. (so remove the <input type="file" ...>)
Or don't use it and manually add the <input> fields, but then you must use the correct names. Your form expects a "file" parameter, not a "sourcefile" parameter:
<input type="file" name="file">
Also, you're overcomplicating things in your view (even though your current code will work if you fix your template):
if form.is_valid():
form.save() # this will save your model
return redirect(...)

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)

Not able to post form data to action url

I have a login form. After pressing the login button the the post data is sent to the view login_auth that authenticates the user data and redirects accordingly. However,after pressing the login button, I am not being redirected to the appropriate page.
views.py
def login_successful(request):
return render(request,"login_successful.html")
def login_invalid(request):
return render(request,"login_invalid.html")
def login(request):
return render(request,'login.html',c)
def loginauth(request):
username=request.POST.get("username",'')
password=request.POST.get("password",'')
user=auth.authenticate(username=username,password=password)
if user is not none:
user.login(request.user)
return redirect(login_successful)
else:
return redirect(login_invalid)
urls.py
urlpatterns = [
url(r'^registration/',views.registration),
url(r'^registration_successful/',views.registration_successful),
url(r'^home/',views.home),
url(r'^login/',views.login),
url(r'^login_successful/',views.login_successful),
url(r'^login_invalid/',views.login_invalid),
url(r'^login/auth',views.loginauth)
]
login.html
<html>
<form action="/login/auth" method="POST">{% csrf_token %}
Username :<input type="textbox" name="username" >
Password :<input type="password" name="password">
<input type="submit" value="Login">
</form>
</html>
Your login url pattern is missing a trailing $. It should be:
url(r'^login/$', views.login),
Without the dollar, the /login/auth is matched by r'^login/, so the request is handled by your login view.
It's a bit unusual to process the form on a different url. Django comes with authentication views, including a login view. I would recommend using this rather than writing your own.
Use name for url
views.py
def login_successful(request):
return render(request,"login_successful.html")
def login_invalid(request):
return render(request,"login_invalid.html")
def login(request):
return render(request,'login.html',c)
def loginauth(request):
username=request.POST.get("username",'')
password=request.POST.get("password",'')
user=auth.authenticate(username=username,password=password)
if user is not none:
user.login(request.user)
return redirect('login_successful')
else:
return redirect('login_invalid')
urls.py
urlpatterns = [
url(r'^registration/',views.registration),
url(r'^registration_successful/',views.registration_successful),
url(r'^home/',views.home),
url(r'^login/$',views.login),
url(r'^login_successful/',views.login_successful, name='login_successful'),
url(r'^login_invalid/',views.login_invalid, name='login_invalid'),
url(r'^login/auth',views.loginauth)
]