For context, here is the view.py
from .forms import SignIn
...
def sign_in_view(request, *args, **kwargs):
form = SignIn(request.POST or None)
if form.is_valid():
form.save()
form = SignIn(request.POST or None)
context = {
'form': form
}
return render(request, "accounts/sign_in.html", context)
here is the HTML file for accounts/sign_in.html
{% extends "accounts/base.html" %}
{% block title %}
<title>Log-in</title>
{% endblock %}
{% block content %}
<h1 align="center">Sign in</h1>
<hr/>
<form method="POST" action="http://127.0.0.1:8000/home/"> # currently in localhost
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Sign in">
</form>
{% endblock content %}
Without the attribute action="http://127.0.0.1:8000/home/" in my form tag in accounts/sign_in.html, I manage to successfully add a user to the database by typing in the required parameters on the browser.
However, with the attribute action="http://127.0.0.1:8000/home/", I was redirected to the URL, but when I checked the database in /admin/, I was unable to see the user there.
I don't know if the form isn't saved, or I had just done some stupid mistake.
Thanks.
Action attribute is not for redirection after sending the form. It should be the url attached to the view that is responsible for handling a form. What is the url of your sign_in_view? If it is for example /sign-in/ that should be value of action attribute. If you want to redirect user after signing in, do it in your sign_in_view:
if form.is_valid():
...
return redirect('/home/')
Here is a link for Django docs that includes all the useful information about forms:
https://docs.djangoproject.com/en/3.0/topics/forms/
This is a better way for the user to register.
view.py
def sign_in_view(request):
form = SignIn(request.POST or None)
if form.is_valid():
user = form.save()
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return redirect('/')
context = {
'form': form
}
return render(request, "accounts/sign_in.html", context)
and than in you template you can do this
{% extends "accounts/base.html" %}
{% block title %}
<title>Log-in</title>
{% endblock %}
{% block content %}
<h1 align="center">Sign in</h1>
<hr/>
<form method="POST" action=""> # currently in localhost
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Sign in">
</form>
{% endblock content %}
Related
After trying for a week to fix the issue I decided to post the question here.
I migrated my application (python+flask) from cloud foundry to Azure.
After that suddenly my user login and registration functionalities stopped working.
After checking a bit more, at first, it was displaying an error message that my CSRF token was invalid.
I disabled CRSF globally to test. The registration function was working again, however, the login functionality was still odd. I showed 4 kinds of behavior:
1 - accessing the Login page directly and inserting the right email and password redirects you to index, as intended, but current_user.is_authenticated = false;
2 - accessing the Login page and inputting the wrong password or email returns you to the login page but with no alert that password or email was wrong;
3- accessing a page behind login requirement and using the right email and password will just reload the login page with the redirect in the URL and do nothing;
4 - same as 3 but with wrong password or email results in the same thing;
After a while, I enabled the CSRF again and now the error message changed to CSRF token missing.
Maybe it is not related, but the issues began after I migrated to Azure (it worked in the beginning, but it stopped working after a while...I can't remember if it was before or after I started enforcing HTTPS).
Here is my code:
init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
import os
from flask_login import LoginManager
from itsdangerous import URLSafeTimedSerializer
app = Flask(__name__)
app.config['MAIL_SERVER'] = os.getenv('MAIL_SERVER')
app.config['MAIL_PORT'] = os.getenv('MAIL_PORT')
app.config['MAIL_USERNAME'] = os.getenv('MAIL_USER')
app.config['MAIL_PASSWORD'] = os.getenv('MAIL_PASSWORD')
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('MYSQL_URI')
app.config['SECRET_KEY'] = os.urandom(32)
app.config['SERVER_NAME'] = os.getenv('SERVER_NAME')
db = SQLAlchemy(app)
db.init_app(app)
migrate.init_app(app, db)
manager = Manager(app)
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)
login = LoginManager()
login.init_app(app)
login.session_protection = "strong"
login_serializer = URLSafeTimedSerializer(app.secret_key)
from app import views, models
if __name__ == '__main__':
app.run(debug=True)
on models.py:
#login.user_loader
def load_user(user_id):
print("User loader called with id %s" % user_id)
return User.query.get(int(user_id))
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
def __repr__(self):
return '<User {}>'.format(self.username)
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
my forms on views.py:
class LoginForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
remember_me = BooleanField('Remember Me')
submit = SubmitField('Sign In')
class RegistrationForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
password2 = PasswordField(
'Repeat Password', validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('Register')
def validate_email(self, email):
user = User.query.filter_by(email=email.data).first()
if user is not None:
raise ValidationError('Please use a different email address.')
my login, register, and logout functions on views.:
#app.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = LoginForm()
print(form.errors)
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password')
return redirect(url_for('login'))
login_user(user, remember=form.remember_me.data)
next_page = request.args.get('next')
if not next_page or url_parse(next_page).netloc != '':
next_page = url_for('index')
return redirect(next_page)
flash('Login Failed')
return render_template('login.html', title='Sign In', form=form, is_auth=current_user.is_authenticated)
#app.route('/logout')
def logout():
logout_user()
return redirect(url_for('index'))
#app.route('/register', methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = RegistrationForm()
print(form.errors)
if form.validate_on_submit():
user = User(email=form.email.data)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
flash('Congratulations, you are now a registered user!')
return redirect(url_for('login'))
return render_template('register.html', title='Register', form=form)
my login template:
{% extends "base.html" %}
{% block content %}
{% endblock %}
{% block body %}
<h1>Sign In</h1>
<div class="box box-default">
<div class="box-body">
{{ form.hidden_tag() }}
<form action="" method="post">
<p>
{{ form.email.label }}<br>
{{ form.email(size=64) }}<br>
{% for error in form.email.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
{% if form.errors %}
{{ form.errors }}
{% endif %}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password(size=32) }}<br>
{% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
{% if form.errors %}
{{ form.errors }}
{% endif %}
</p>
<p>
<p>{{ form.remember_me() }}
{{ form.remember_me.label }}</p>
<p>{{ form.submit() }}</p>
</form>
<p>New User? Click to Register!</p>
</div>
</div>
{% endblock %}
my register template:
{% extends "base.html" %}
{% block content %}
{% endblock %}
{% block body %}
<h1>Register</h1>
<div class="box box-default">
<div class="box-header with-border">
<form action="" method="post">
{{ form.hidden_tag() }}
<p>
{{ form.email.label }}<br>
{{ form.email(size=64) }}<br>
{% for error in form.email.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
{% if form.errors %}
{{ form.errors }}
{% endif %}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password(size=32) }}<br>
{% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
{% if form.errors %}
{{ form.errors }}
{% endif %}
</p>
<p>
{{ form.password2.label }}<br>
{{ form.password2(size=32) }}<br>
{% for error in form.password2.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
{% if form.errors %}
{{ form.errors }}
{% endif %}
</p>
<p>{{ form.submit() }}</p>
</form>
</div>
</div>
{% endblock %}
I apologize in advance if the code doesn't look professional, I'm still a junior and a bit new with flask.
Does anyone see what might be causing the described issue?
I'm using python 3.8, flask 1.1.2, flask-wtf 0.14.3, and flask-login 0.5.0.
I tried everything from the other similar questions on StackOverflow and from this blog, but nothing worked so far or seems to be related to my issue (so please consider this before marking this question as a duplicate).
Thank you very much in advance for your help.
edit: grammar
I talked with Miguel Grinberg about the issue and he pointed out the problem was with:
app.config['SECRET_KEY'] = os.urandom(32)
This part of the code was making so, every time the app was restarted the tokens would be invalidated.
I just changed it to a static environment variable and it is now working fine again.
Code snippets that I was able to add so far:
Model class:
class MboxForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MboxForm, self).__init__(*args, **kwargs)
csv_file = forms.FileField(required=True,
label="Select CSV file")
class Meta:
model = Mbox
fields = ['some_field',
]
widgets = {
'csv_file': forms.FileInput(),
}
In admin.py:
class MboxAdmin(admin.ModelAdmin):
actions = [import_from_csv,
export_to_csv]
def get_form(self, request, obj=None, **kwargs):
kwargs['form'] = MboxForm
return super().get_form(request, obj, **kwargs)
def get_urls(self):
urls = super().get_urls()
my_urls = [
path("upload_csv",
self.upload_csv, name='upload_csv')
]
return my_urls + urls
urls = property(get_urls)
def upload_csv(self, request):
if request.method == 'POST':
form = MboxForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('admin/change_list.html')
else :
return render(request, 'admin/change_list.html',
{'form': form,
'opts': Mbox._meta})
else:
return render(
request,
'admin/change_list.html',
context
)
def changelist_view(self, request, extra_context=None):
extra_context = {}
extra_context['submit_csv_form'] = MboxForm
if 'action' in request.POST and request.POST['action'] == 'import_from_csv':
if not request.POST.getlist(admin.ACTION_CHECKBOX_NAME):
post = request.POST.copy()
for u in Mbox.objects.all():
post.update({admin.ACTION_CHECKBOX_NAME: str(u.id)})
request._set_post(post)
return super().changelist_view(request, extra_context=extra_context)
This button displays in place of the add button in the listing page with the template with the file browser to upload any file.
{% extends "admin/change_list.html" %}
{% load i18n admin_urls static admin_list %}
{% block object-tools %}
{% if has_add_permission %}
{% block object-tools-items %}
<form id="upload-csv-form" method="POST"
action="{% url 'admin:upload_csv' %}"
method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
{% comment %}
<p>{{ submit_csv_form.as_p }}</p>
{% endcomment %}
<p>{{ submit_csv_form.csv_file.errors }}</p>
<p><input type="file" value="CSV file" />
<p><input type="submit" value="Upload" />
<!-- input type="reset" value="Reset"> -->
</form>
{% endblock %}
</ul>
</div>
{% endif %}
{% endblock %}
The error That I get on clicking the upload button:
NoReverseMatch
Exception Value:
Reverse for 'app_list' with keyword arguments '{'app_label': ''}' not
found. 1 pattern(s) tried: ['admin/(?P<app_label>auth|mailmanager)/$'
mailmanager is the app for which the model: Mbox exists. Besides where do I place this change_view.html template in the template structure for only this model. I tried placing the template under myproject/templates/admin/templates/mailmanager/mbox, but it is not found. Also I don't wish to replace the 'Add model' button but have this placed next to it in the default model change list template.
I have followed the stack link so far but not got it working as yet. :Upload CSV file in django admin list view, replacing add object button Please guide .
I want to create a dynamic input form and read the user inputs back when a button ("Submit") is pressed, what is the best way (a good way) to do this?
I have tried creating a dynamic form with a FieldList but I was unable to get the values from the user.
This is (hopefully all of) the relevant code:
forms.py
class EntryForm(Form):
field = StringField()
class DynamicForm(FlaskForm):
parameter = FieldList(FormField(EntryForm), min_entries=1)
submit = SubmitField('Submit')
routes.py
#app.route("/new", method=['GET', 'POST'])
def new_form():
form = DynamicForm()
if form.validate_on_submit():
values = form.parameter #This does not work as intended.
do_stuff(values)
return redirect(url_for('index'))
parameter = utils.get_parameter()
return render_template('dynamic_form.html', form=form, parameter=parameter)
where utils.get_parameter() returns an unknown list of parameters. Or to be precise the function expects a parameter and returns a list based on this paramater, but I have omitted this here.
dynamic_form.html
{% extends "layout.html" %}
{% macro render_field(item) %}
<div class="form-group">
<label class="form-control-label">{{ item.label }}</label>
<input type="text" name="{{ item.name }}" class="form-control" value="{{ item.value }}"/>
</div>
{% endmacro %}
{% block content %}
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class ="form-group">
<legend class="border-bottom mb-4">Parameter</legend>
{% for item in form.parameter %}
{{ render_field(item) }}}
{% endfor %}
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-outline-info") }}
</div>
</form>
</div>
{% endblock content %}
I assume it is because the validate_on_submit() is called after the form is constructed (again) and thus it does not contain the form fields, and especially not the user inputs. Is there a way to prevent this "data loss" or is this a completely wrong approach?
Also, please let me know if information is missing.
You don't want to validate the form when the page first loads. Try this first:
if request.method == "POST" and form.validate_on_submit():
values = form.parameter
do_stuff(values)
return redirect(url_for('index'))
Next, notice that you are redirecting them to your view for def index():. Is that what you want?
I have a form that is setup as below and ultimately, the error I get in debug mode is sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed. I understand why, but I would like this error to get presented to the user so they know the issue.
class RegistrationForm(FlaskForm):
company_url = StringField('Company Website', validators=[DataRequired(), URL(message='Must be a valid URL')])
password = PasswordField(
'Password', validators=[DataRequired(), EqualTo('pass_confirm',
message='Passwords must match')]
)
pass_confirm = PasswordField('Confirm Password', validators=[DataRequired()])
submit = SubmitField('Register!')
However, when an incorrect URL structure is entered into the form and submitted, I cannot get it to flash an error message in the html page for the user. The html page is below. I will place the base.html below as well.
{% extends "base.html" %}
{% block content %}
<form method="POST">
{{form.hidden_tag()}}
{{form.company_url.label}}{{form.company_url()}}<br>
{{form.password.label}}{{form.password()}}<br>
{{form.pass_confirm.label}}{{form.pass_confirm()}}<br>
{{form.submit()}}
</form>
{% endblock %}
<!DOCTYPE html>
<html>
<head>
...some code here
</head>
<body>
<!-- Just an image -->
<nav class="navbar navbar-dark bg-dark">
<span class="align-bottom text-light">Creating connections, saving time</span>
</nav>
{% with messages = get_flashed_messages() %} {% if messages %} {% for
message in messages %}
<div class="alert alert-warning alert-dismissible" role="alert">
<button
type="button"
class="close"
data-dismiss="alert"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
{{message}}
</div>
{% endfor %} {% endif %} {% endwith %} {% block content %} {% endblock %}
</body>
</html>
The views.py is:
#app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
user = Buyers(
company_url=form.company_url.data,
password=form.password.data
)
db.session.add(user)
db.session.commit()
flash("Thank you for registering.")
return redirect(url_for('login'))
return render_template('register.html', form=form)
How do I error handle in forms to make sure the user is aware that they have entered a URL with an incorrect structure? I am in debug mode and right now, the register.html page just basically reloads without any message as to why. Thank you!
views.py :
import validators
#app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
isUrlValid=validators.url(form.company_url.data)
if isUrlValid==True:
user = Buyers(
company_url=form.company_url.data,
password=form.password.data
)
db.session.add(user)
db.session.commit()
flash("Thank you for registering.")
return redirect(url_for('login'))
else:
flash("URL is not valid. Please enter a valid URL")
return redirect(url_for('register'))
return render_template('register.html', form=form)
I was running some tests in Django to see if form.errors raises all types of errors in the form (which it does).
Now here's where things went south:
If i try to sign up with an existing email/username (just checking the efficiency) more than once , i get this
Forbidden (403)
CSRF verification failed. Request aborted.
Help text : (all those conditions are met.)
I think these tests break the csrf_token.
So i don't know if the problem is coming from my code or the csrf_token is just doing its job by protecting the owner of that username/email.
Did anyone encounter an issue like this before ?
SignUp View
class SignUp(View):
def get(self, request):
form = MyModelCreation()
return render(
request,
'signup.html',
{'form': form}
)
def post(self, request):
form = MyModelCreation(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.is_active = False # Create an inactive user
user.save()
# Send a confirmation Email
# Generate a token for the new user --tokens.py--
current_site = get_current_site(request)
mail_subject = 'Activate your profile account.'
message = render_to_string('account_activation_email.html', {
'user': user,
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode(),
'token': user_token.make_token(user),
})
receiver = form.cleaned_data.get('email')
email = EmailMessage(
mail_subject, message, to=[receiver]
)
email.send()
return redirect("account_activation_sent")
else:
return render_to_response(
'signup.html',
{"form": form},
RequestContext(request)
)
SignUp Template
{% extends 'base_test.html' %}
{% block title %}My Site | Sign Up{% endblock title %}
{% block content %}
<div class="padding">
<h2>Sign up : <small>*ALL FIELDS ARE REQUIRED</small></h2>
<form method="post" class="form">
{% csrf_token %}
{% for field in form %}
<p>
{{ field.label_tag }}<br>
{{ field }}
{% for error in field.errors %}
<p style="color: red">{{ error }}</p>
{% endfor %}
</p>
{% endfor %}
<button class="btn btn-primary btn-lg" type="submit">Sign up</button>
</form>
</div>
{% endblock %}
well i fixed it by switching back to render(), i think render_to_response() requires some additional data that i don't know of.
return render(request, 'signup.html', {'form': form})
Thank you !