How can I keep field data after validating false in flask wtform? - forms

I am new to flask and don't know how to keep field data after a failing post.
Thanks for your helps ^_^.
Example:
views.py:
#app.route('/', methods=['GET', 'POST', ])
def index():
form = MyForm()
if request.method == 'GET':
return render_template('index.html', form=form)
elif request.method == 'POST':
if form.validate_on_submit():
# blabla...
return redirect('/')
else: # validate false
# how to keep field data in new page?
return render_template('index.html', form=form) # it failed

It didn't work because I implement my own html form fields, in order to solve the problem, I should write template like this:
index.html:
<form ...>
{{ form.fieldname(class_='form-control', placeholder='hint') }}
</form>

Instead of inheriting your Form class from wtforms.Form, inherit it from flask_wtf.FlaskForm
For example, Replace this
from wtforms import Form
class RegistrationForm(Form):
#fields...
With this
from flask_wtf import FlaskForm
class RegistrationForm(FlaskForm):
#fields...

Related

django form to upload file returns error as form not valid

I am developing an app in Django.
My users are allowed to save data by compiling a form like this
Tool:
acronym:
definition:
defined by the following function, in forms.py:
class tool_form(forms.ModelForm):
class Meta:
model=tool
fields=["Tool", "Acronym", "Definition"]
That saves the data into a model like this:
class tool(models.Model):
Tool = models.CharField(max_length=256, blank=True, null=True)
Acronym = models.CharField(max_length=25, blank=True, null=True)
Definition = models.TextField(blank=True, null=True)
The view function allowing this, is:
def add_tool(request):
if request.method=='POST':
form = tool_form(request.POST or None)
if form.is_valid():
form.save()
messages.success(request, ("Submit succeed!"))
return redirect('adding_tools')
else:
messages.error(request, ('ERROR: submit failed'))
return render(request, 'adding_tools.html', {})
else:
return render(request, 'adding_tools.html', {})
Now I want my users to be able to copile many times of the same form, all at once.
In order to achieve this, I am allowing my users to upload a file copiled with the data to insert.
So I am allowing my users to download a template xlsx file with colums with given name
Column 1 name (cell A1): Tool
Column 2 name (cell B1): acronym
Column 3 name (cell C1): definition
To compile it, inserting many records, and then to upload it back.
So I want my code to save this data into the same model declared before (tool)
I am trying to achieve this by:
in template add_tool_sheet.html:
<form class="container" method="POST" enctype="multipart/form-data" >
{% csrf_token %}
<div class="file-upload-wrapper" id="input-file-now">
<small id="inputHelp" class="form-text text-muted">Select file to upload.</small>
<input type="file" name="uploaded_file" id="input-file-now" data-max-file-size="5M" class="file-upload">
<br><br>
<div class="form-group">
<input name="Date" type="hidden" class="form-control" id="date_to_turn_into_toda">
</div>
<button type="submit" class="btn btn-primary">Upload</button>
</div>
</form>
in forms.py:
class tool_file_form(forms.ModelForm):
class Meta:
model=tool_file
fields=["Tool_file", "Date"]
In models.py
class tool_file(models.Model):
Tool_file = models.FileField(upload_to='uploaded_sheets/', blank=False, null=False)
Date = models.DateField(blank=False, null=False, default=timezone.now().date() )
class Meta:
ordering = ['Date', 'Tool_file']
def clean(self):
if not (self.Tool_file or self.Date):
raise ValidationError("something went wrong")
def __str__(self):
return "%s ----- [%s]" % (self.Tool_file, self.Date)
in views.py:
def add_tool_sheet(request):
if request.method=='POST':
form = tool_file_form(request.POST, request.FILES)
if form.is_valid():
form.save()
messages.success(request, ("upload succeeded"))
return redirect('add_tool_sheet')
else:
messages.error(request, ('ERROR n1'))
return render(request, 'add_tool_sheet.html', {})
else:
return render(request, 'add_tool_sheet.html', {})
When I try to add new objects in the model tool_file from admin section, it works.
But when I try to add new objects from the user interface (template add_tool_sheet.html), it returns
ERROR n1
as message, and my console returns
GET /admin/ HTTP/1.1" 200 7381
Why?
Please note:
The upload from admin section works, the upload from user interface does not.
SOLVED
In template I put
name="uploaded_file"
but in order to match with the information in forms.py and model.py, it has to be:
name="Tool_file"
Now it works!

Flask Error: “Method Not Allowed The method is not allowed for the requested URL” (Login and Home Page)

#app.route("/")
#app.route("/home",methods=['GET', 'POST'])
def home():
if current_user.is_authenticated:
posts=mongo.db.articles
#allpost=posts.find().limit(5)
it=current_user.user_json['interest']
allpost=posts.find( {'NewsType': it } ).limit(10)
#flash(session['email'])
return render_template('home.html', posts=allpost)
return render_template('login.html', title='Login',form=LoginForm())
This is my code for the Home Page
#app.route("/login", methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('home'))
form = LoginForm()
if form.validate_on_submit():
users = mongo.db.users
loginuser_json = users.find_one({'email': form.email.data})
if loginuser_json and bcrypt.check_password_hash(loginuser_json['password'], form.password.data):
# Create a custom user and pass it to login_user:
loginuser = User(loginuser_json)
login_user(loginuser,duration=d)
next_page = request.args.get('next')
return redirect(next_page) if next_page else redirect(url_for('home'))
return redirect(url_for('home'))
else:
flash('Login Unsuccessful. Please check username and password', 'danger')
return render_template('login.html', title='Login', form=form)
and this for the login route
When i write localhost:5000 in the browser it opens Login Page (because if user is not authenticated it should go to login page first)
But when i try to login from this page , it is giving an error "Method Not Allowed.The method is not allowed for the requested URL."
What should i do
In your code, it is not clear which code is called when the method is GET (usually when rendering your login page) and which is to be called when method is POST (called when you are submitting your form). To remove the mix up, do like this in your login
#app.route("/login", methods=['GET', 'POST'])
def login():
# code that runs for both GET and POST goes here
if request.method == 'POST':
if form.validate_on_submit():
#authenticate user and redirect to next page/home page
return render_template('login.html', title='Login', form=form) #this runs when method is get
Note the indentation and the respective code running under it
NOTE on your login template ensure the form method is post something like this
<form method=post>
<dl>
{{ render_field(form.email) }}
{{ render_field(form.password) }}
{{form.hidden_tag()}}
</dl>
<input type=submit value=Submit>
</form>
Lastly, on you home page, you have two routes that form the url_for(home) - though I do not think this is where your error is coming from - ensure both of them accept the method POST if you intend to use it there
#app.route("/",methods=['GET', 'POST'])#add post on this route
#app.route("/home",methods=['GET', 'POST'])
def home():
Hope this helps you sort out the error

Django2 forms nothing happend when submit post article

I have forms to Post Article in my blog Django2. When I run django server there is no error, but when I Post and submit Article in my frontEnd apps nothing happens and doesn't save any data.
Any help on this would be highly appreciated!
Template HTML
<div class="create-article">
<h2>Create an Awesome New Articles</h2>
<form class="site-form" action="{% url 'articles:create' %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form }}
<input type="submit" value="Create">
</form>
</div>
forms.py
from django import forms
from . import models
class CreateArticle(forms.ModelForm):
class Meta:
model = models.Article
fields = ['title', 'body', 'slug', 'thumb']
views.py
def article_create(request):
if request.method == 'POST':
form = forms.CreateArticle(request.POST, request.FILES)
if form.is_valid():
# save article to db
instance = form.save(commit=False)
instance.author = request.user
instance.save
return redirect('articles:list')
else:
form = forms.CreateArticle()
return render(request, 'articles/article_create.html', {'form': form})
urls.py
from django.urls import path
from . import views
app_name = 'articles'
urlpatterns = [
path('', views.article_list, name="list"),
path('create/', views.article_create, name="create"),
path('<slug>/', views.article_detail, name="detail"),
]
Thanks.
It starts here: instance = form.save(commit=False), where you are not commiting the save.
Further down you have instance.save as if you were setting a model field, but with not value given to it.
Make that line instance.save() instead.

Django 2 UserCreationForm not creating user

I'm using django-email-as-username so users can authenticate without a username and use their email instead. It seems that when I try to register a new user, I'm redirected back to the form page and the user isn't created. Am I supposed to be including the cleaned_data in my view?
Any advice?
forms.py
from django import forms
from django.contrib.auth import (authenticate, get_user_model,
password_validation)
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.utils.translation import ugettext_lazy as _
from cuser.models import CUser
UserModel = get_user_model()
class AuthenticationForm(forms.Form):
"""
Base class for authenticating users. Extend this to get a form that accepts
email/password logins.
"""
email = forms.EmailField(
label=_("Email address"),
max_length=254,
widget=forms.EmailInput(attrs={'autofocus': True}),
)
password = forms.CharField(
label=_("Password"),
strip=False,
widget=forms.PasswordInput,
)
error_messages = {
'invalid_login': _(
"Please enter a correct %(username)s and password. Note that both "
"fields may be case-sensitive."
),
'inactive': _("This account is inactive."),
}
def __init__(self, request=None, *args, **kwargs):
"""
The 'request' parameter is set for custom auth use by subclasses.
The form data comes in via the standard 'data' kwarg.
"""
self.request = request
self.user_cache = None
super().__init__(*args, **kwargs)
self.username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD)
def clean(self):
email = self.cleaned_data.get('email')
password = self.cleaned_data.get('password')
if email and password:
self.user_cache = authenticate(self.request, email=email, password=password)
if self.user_cache is None:
# An authentication backend may reject inactive users. Check
# if the user exists and is inactive, and raise the 'inactive'
# error if so.
try:
self.user_cache = UserModel._default_manager.get_by_natural_key(email)
except UserModel.DoesNotExist:
pass
else:
self.confirm_login_allowed(self.user_cache)
raise forms.ValidationError(
self.error_messages['invalid_login'],
code='invalid_login',
params={'username': self.username_field.verbose_name},
)
else:
self.confirm_login_allowed(self.user_cache)
return self.cleaned_data
def confirm_login_allowed(self, user):
"""
Controls whether the given User may log in. This is a policy setting,
independent of end-user authentication. This default behavior is to
allow login by active users, and reject login by inactive users.
If the given user cannot log in, this method should raise a
``forms.ValidationError``.
If the given user may log in, this method should return None.
"""
if not user.is_active:
raise forms.ValidationError(
self.error_messages['inactive'],
code='inactive',
)
def get_user_id(self):
if self.user_cache:
return self.user_cache.id
return None
def get_user(self):
return self.user_cache
class UserCreationForm(forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given email and
password.
"""
error_messages = {
'password_mismatch': _("The two password fields didn't match."),
}
email = forms.EmailField(
label=_("Email address"),
max_length=254,
widget=forms.EmailInput(attrs={'autofocus': True}),
)
password1 = forms.CharField(
label=_("Password"),
strip=False,
widget=forms.PasswordInput,
help_text=password_validation.password_validators_help_text_html(),
)
password2 = forms.CharField(
label=_("Password confirmation"),
widget=forms.PasswordInput,
strip=False,
help_text=_("Enter the same password as before, for verification."),
)
class Meta:
model = CUser
fields = []
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',
)
return password2
def _post_clean(self):
super()._post_clean()
# Validate the password after self.instance is updated with form data
# by super().
password = self.cleaned_data.get('password2')
if password:
try:
password_validation.validate_password(password, self.instance)
except forms.ValidationError as error:
self.add_error('password2', error)
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
email = forms.EmailField(
label=_("Email address"),
max_length=254,
widget=forms.EmailInput(),
)
password = ReadOnlyPasswordHashField(
label=_("Password"),
help_text=_(
"Raw passwords are not stored, so there is no way to see this "
"user's password, but you can change the password using "
"this form."
),
)
class Meta:
model = CUser
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['password'].help_text = self.fields['password'].help_text.format('../password/')
f = self.fields.get('user_permissions')
if f is not None:
f.queryset = f.queryset.select_related('content_type')
def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial["password"]
views.py
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect
from django.urls import reverse
from .forms import UserCreationForm
def index(request):
return HttpResponse("This will be the profile homepage.")
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts')
else:
form = UserCreationForm()
return render(request, 'accounts/register.html', {'form': form})
urls.py
from django.urls import path
from django.conf.urls import include, url
from accounts import views
urlpatterns = [
path('', views.index, name='index'),
path('register/', views.register, name='register'),
register.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div>
<h1>Register</h1>
<form method="post" action="">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
</div>
</body>
</html>
Have you looked at custom user documentation? Seems to mention exactly what you mentioned, but as a caveat it requires you to do it on the first migration

Redirects in Flask/Werkzeug are not changing the URL

I am very knew to python web development, so please bear with me.
I am trying setup a very basic log-in using Flask and the below code:
#app.route('/')
def index():
if verifyLoggedIn():
someData = gatherSomeData()
return render_template('main.html', data=someData)
else:
return redirect(url_for('login'))
#app.route('/login/', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
if request.form['usr'] == user and request.form['pwd'] == password:
session['user'] = request.form['usr']
session['passwd'] = request.form['pwd']
return redirect(url_for('index'))
else:
return render_template('out.html',name=request.form['usr'])
else:
return render_template('login.html')
When I access the page by going to 'localhost:5000/', I correctly get forwarded to
'localhost:5000/login/'. After I log-in, however, the page loads 'main.html' but the url bar still shows 'localhost:5000/login/'. The problem with this, is that if I hit refresh button to get the new value of 'someData' I end up back at the log-in screen. I find that this is the case after any post action. Am I missing something very fundamental here?
Thanks to those who responded, but after much more searching, I managed to find that the answer was already on stackoverflow :p (sorry, I really did look around a lot before asking)
Url in browser not updated after call of redirect( url_for('xxx' )) in Flask with jQuery mobile
The problem was actually being caused by jquery mobile and my lack of a data-url attribute.
The following code works for me, it's basically the same as what you're doing:
from flask import Flask, request, render_template, session, redirect, url_for
app = Flask(__name__)
app.config['SECRET_KEY'] = 'sldkf'
#app.route('/')
def index():
if session['user']:
return render_template('main.html')
else:
return redirect(url_for('login'))
#app.route('/login/', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
user = 'test'
password = 'test'
if request.form['usr'] == user and request.form['pwd'] == password:
session['user'] = request.form['usr']
session['passwd'] = request.form['pwd']
return redirect(url_for('index'))
else:
return render_template('login.html')
else:
return render_template('login.html')
#app.route('/logout/', methods=['GET'])
def logout():
session['user'] = None
return redirect(url_for('login'))
if __name__ == '__main__':
app.run(debug=True)
You should check to see if the user is authenticated in your 'login' route. If so redirect to the 'index' route using return redirect(url_for('index')).