I made a form to reset password, when I submit the form with an empty password, the error prompt words I set in views.py didn't show up at the <span> I left in a HTML, a default Fill out this field showed instead.
*fisrt one is old password, second one is new password
In forms.py:
class PwdForm(FlaskForm):
old_pwd = PasswordField(
label="OldPassword",
validators=[
DataRequired("Please input old password")
]
)
submit = SubmitField(
"Confirm"
)
In views.py:
#admin.route("/pwd_reset/", methods=["GET", "POST"])
#admin_login_req
def pwd_reset():
form = PwdForm()
if form.validate_on_submit():
data = form.data
admin = Admin.query.filter_by(name=session["admin"]).first()
from werkzeug.security import generate_password_hash
admin.pwd = generate_password_hash(data["new_pwd"])
db.session.add(admin)
db.session.commit()
flash("ok, now use your new password to login", "ok")
redirect(url_for("admin.logout"))
return render_template("admin/pwd_reset.html", form=form)
In html:
<label for="input_pwd">{{ form.old_pwd.label }}</label>
{{ form.old_pwd }}
{% for err in form.old_pwd.errors %}
<span style="color: #ff4f1f">{{ err }}</span>
{% endfor %}
How to make my own prompt message show up
I think you mean how do you get your flash message to display? Use the following code in your base template page or in each page. See: Message Flashing
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flashes">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
Related
I am using form validation to validate my password to see if they match and it works. However, because {{ form.errors }} always return the label of affected input field, i resulted to using the answer provided by Samsparx below. however, it does not return errors form my forms.py.
here is my forms.py validation
def clean(self):
super(Signup, self).clean()
password = self.cleaned_data.get("password")
password2 = self.cleaned_data.get("password2")
username = self.cleaned_data.get("username")
email = self.cleaned_data.get("email")
avatar = self.cleaned_data.get("avatar", False)
if password != password2:
self.errors[""] = self.error_class(["Password mismatch"])
here is my template
<div>
{% for field in form %}
{% for error in field.errors %}
<p>
<ul>
<li>{{ error }}</li>
</ul>
</p><br>
{% endfor %}
{% endfor %}-->
</div>
Whenever i use this above template to return error, it doesn't display the Password Mismatch error that forms.py returns. If i however use {{form.errors}}, it displays the Password Mismatch error and also displays the label of input field affected which i do not want.
So what i want to know is how do i get {{form.errors}} to display any error without displaing label of affect input field.
Errors are also listed by field in the form, so you can loop through with this to avoid mentioning the fieldname:
{% for field in form %}
{% for error in field.errors %}
{{ error }}<br>
{% endfor %}
{% endfor %}
{{form.errors}} is a dictionary which save error fields in key value pair. So i solved my problem by looping through it using the code below.
{% for key, val in form.errors.items %}
<p>{{val}}</p>
{% endfor %}
I have following scenario.
User fills out a form
If the user clicks the "continue" button and the form is valid the user will be redirected to a summary view
In the summary view the user checks the input again. He can either continue or go back.
If he continues the data will be saved in the database, if he goes back he can edit the form.
Since in step 4 the user is at the view summary I have to redirect him to the home view. I don´t want the user to fill out the entire form again, the previously entered data should be autofilled if he goes back.
Something special: I am using django-tagify2 for one input in the form to get tags rather then text. If the user goes back the tags should be rendered correctly in the tagify specific form.
So here are my files:
home.html
{% extends "messenger/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="message-container">
<form method="POST" autocomplete="off">
{% csrf_token %}
{{ form|crispy }}
<button name="sendmessage" class="btn btn-outline-info" type="submit">Continue</button>
</form>
</div>
{% endblock content %}
summary.html
{% extends "messenger/base.html" %}
{% block content %}
<h4>Zusammenfassung</h4>
<p><b>Empfänger: </b>{{ receiver }}</p>
<br>
<p><b>Betreff: </b>{{ subject }}</p>
<br>
<p><b>Nachricht: </b>{{ message }}</p>
<button>Edit</button>
<button>Continue</button>
{% endblock content %}
home view
#login_required(login_url='login')
def home(request):
if request.method == 'POST' and 'sendmessage' in request.POST:
message_form = MessageForm(request.POST)
if message_form.is_valid():
receiver_list = message_form['receiver'].value().split(';')
subject = message_form['subject'].value()
message = message_form['textmessage'].value()
#create sessions and send data to next view
session_receiver = receiver_list
request.session['session_receiver'] = session_receiver
session_subject = subject
request.session['session_subject'] = session_subject
session_message = message
request.session['session_message'] = session_message
return redirect('summary')
else:
message_form = MessageForm()
return render(request, 'messenger/home.html', {'form': message_form})
summary view
def summary(request):
receiver = request.session.get('session_receiver')
subject = request.session.get('session_subject')
message = request.session.get('session_message')
return render(request, 'messenger/summary.html', {'receiver':receiver,
'subject':subject,
'message':message})
So what is the best way to do this?
Can I use the session variables to set the fields in the form?
I don´t want to change the logic in the app. I want a home/summary/success view/template where I can loop as long is I want between home and summary until the user is happy with his entered form data
How about checking request.session when there is get request to home view? Then you can bind message_form = MessageForm() to session data.
You can check out htmx and django-htmx. You can do what you want easily without session by swapping HTML with context.
I played around with the session values and views and finally got a way to redirect to other views with prefilled form fields based on session values.
#login_required(login_url='login')
def home(request):
if request.method == 'POST' and 'sendmessage' in request.POST:
message_form = MessageForm(request.POST)
if message_form.is_valid():
ad_group = message_form['ad_group'].value().split(';')
ad_user = message_form['ad_user'].value().split(';')
subject = message_form['subject'].value()
message = message_form['textmessage'].value()
#create sessions and send data to next view
session_ad_group = ad_group
request.session['session_ad_group'] = session_ad_group
session_ad_user = ad_user
request.session['session_ad_user'] = session_ad_user
session_subject = subject
request.session['session_subject'] = session_subject
session_message = message
request.session['session_message'] = session_message
return redirect('summary')
else:
if request.session.get('session_subject'):
message_form = MessageForm(initial={'ad_group': request.session.get('session_ad_group'),
'ad_user': request.session.get('session_ad_user'),
'subject': request.session.get('session_subject'),
'textmessage': request.session.get('session_message')})
return render(request, 'messenger/home.html', {'form': message_form})
else:
message_form = MessageForm()
return render(request, 'messenger/home.html', {'form': message_form})
def summary(request):
ad_group = request.session.get('session_ad_group')
ad_user = request.session.get('session_ad_user')
subject = request.session.get('session_subject')
message = request.session.get('session_message')
if request.method == 'POST' and 'edit' in request.POST:
message_form = MessageForm(initial={'ad_group':ad_group, 'ad_user': ad_user,
'subject':subject, 'textmessage':message})
return render(request, 'messenger/home.html', {'form': message_form})
return render(request, 'messenger/summary.html', {'ad_group':ad_group,
'ad_user': ad_user,
'subject':subject,
'message':message})
Template
{% extends "messenger/base.html" %}
{% block content %}
<h2>Zusammenfassung</h2>
<div class="border-top pt-3">
<p><b>AD-Gruppe: </b>{{ ad_group }}</p>
<p><b>AD-User: </b>{{ ad_user }}</p>
<br>
<p><b>Betreff: </b>{{ subject }}</p>
<br>
<p><b>Nachricht: </b>{{ message }}</p>
<div class="buttoncontainer">
<form name="edit" action="" method="post">
{% csrf_token %}
<button class="btn edit_btn" formaction="{% url 'messenger-home' %}">Zurück</button>
</form>
<form name="senden" action="" method="post">
{% csrf_token %}
<button class="btn continue_btn" formaction="{% url 'send_messages' %}">Nachricht senden</button>
</form>
</div>
</div>
{% endblock content %}
I have multiple form on a page in modals: 1-st to create a new user address and n-forms with addresses which user created (created with loop). When i enter invalid data in fields with validators (e.g. datarequired), i have error messages in each form.
Here is the field render example which i use in every form:
{{ address_form.street.label(class_="form-label", for="InputStreet") }}
{{ address_form.street(class_="form-control", id="InputStreet") }}
{% for error in address_form.street.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
Part of the code from view.py:
#bp.route('/profile/address', methods=['GET', 'POST'])
#login_required
def address():
address_form = AddressForm()
if address_form.submit_address.data and address_form.validate():
address_to_add = Address(
street=address_form.street.data,
house=address_form.house.data,
building=address_form.building.data,
entrance=address_form.entrance.data,
floor=address_form.floor.data,
apartment=address_form.apartment.data,
additional_info=address_form.additional_info.data,
user=current_user)
db.session.add(address_to_add)
db.session.commit()
return redirect(url_for('profile.address'))
if address_form.edit_address.data and address_form.validate():
address_to_edit = Address.query.get(address_form.address_id.data) # Here is data from hidden field
# Editing data in DB
db.session.commit()
return redirect(url_for('profile.address'))
return render_template('profile/address.html', title='Адрес доставки', address_form=address_form)
Forms work fine with adding, editing and deleting data, but work incorrect with validation errors.
I think i need one more condition in if statenent related with hidden field or change something in my html file.
I've tried add an action attr in form like:
<form action="{{ url_for('profile.address', form_id=address.id) }}" method="post" novalidate>
And smth like this in view func but it doesn't work:
form_id = request.args.get('form_id', type=int)
if address_form.edit_address.data and address_form.validate() and form_id == address_form.address_id.data:
pass
Finally i found a very bad solution:
address_form = AddressForm()
form_id = request.args.get('form_id', 0, type=int)
For main form form_id always is 0.
Form tag for main form:
<form action="{{ url_for('profile.address', form_id=form_id) }}" method="post" novalidate>
And a field render for main form:
{{ address_form.street.label(class_="form-label", for="InputStreet") }}
{{ address_form.street(class_="form-control", id="InputStreet") }}
{% if form_id == 0 %}
{% for error in address_form.street.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
{% endif %}
If someone has a better solution about my problem it'd good. Now it's time to learn some JS and solve this problem with AJAX.
I am tring to sent email with django using gmail smtp server i write settings in setting.py.here is my other code but I am getting AttributeError at /share/4 'str' object has no attribute 'get' ? please help me to solve that error.
**forms.py**
from django import forms
class EmailPostForm(forms.Form):
name = forms.CharField(max_length=100)
email = forms.EmailField()
to = forms.EmailField()
comment = forms.CharField(widget=forms.Textarea, required=False)
views.py
def share_email(request, id):
post = get_object_or_404(Post, id=id)
sent = False
if request.method == 'POST':
form = EmailPostForm(data=request.method)
if form.is_valid():
cd = form.cleaned_data
post_url =
request.build_absolute_uri(post.get_absolute_url())
subject = '{} ({}) recommend you reading "{}"'.format(cd['name'], cd['email'], post.title)
message = 'Read "{}" at {}\n\n{}\'s comments: {}'.format(post.title, post_url, cd['name'], cd['comment'])
send_mail(subject, message, 'admin#gmail.com', cd['to'])
sent = True
else:
form = EmailPostForm()
return render(request, 'blog/post/share_post.html', {'post': post, 'form': form, 'sent': sent})
url.py
urlpatterns = [path('share/<int:id>', views.share_email, name='share_post'),]
share_post.html
{% extends 'blog/base.html' %}
{% block title %}
Share Post
{% endblock %}
{% block content %}
{% if sent %}
<h2>E-mail successfully sent</h2>
<p>{{ post.title }} is successfully dent by email</p>
{% else %}
<h2>Share {{ post.title }} by email</h2>
<form action="{% url 'blog:share_post' post.id %}" method="post">
{{ form.as_p }}
{% csrf_token %}
<input type="submit" value="Send Email">
</form>
{% endif %}
{% endblock %}
Here:
form = EmailPostForm(data=request.method)
you want request.POST, not request.method.
As a side note: a successful post should be followed by a redirect (to prevent page reload to repost the same data). You can use the contrib.messages app to set a message in session, that will then be displayed on the next page.
I've edited my question to be more clear. When I validate my form and it has errors, I flash my message but this does not end up rendered in my template:
#app.route('doit', methods=["GET", "POST"])
def doit():
form = MyForm()
if form.validate_on_submit():
flash('success')
else:
if form.errors:
print "You've got errors!"
flash('You have some errors')
print session['_flashes']
return render_template('test.html')
My template for displaying messages:
{% with messages = get_flashed_messages() %}
{{ messages }}
<br/>
{{ session }}
{% endwith %}
When I submit my form with errors, I flash flash("You have some errors"), and I DO see _flashes in the session holding my error message when I print my session to console:
# my console output
You've got errors!
[('message', 'You have some errors')]
However, when the template renders, {{ session }} does not have _flashes at all, and so get_flashed_messages() is always an empty list. No message is flashed as a result.
What am I doing wrong?
Okay guys, I was being rather dumb (again). Turns out the form was being POSTed through AJAX, but the result of the call was was expecting a JSON format and not the entire HTML template.
I've switched to returning a json response instead, and now it is fine.
The function get_flashed_messages() returns a list, which you should iterate over and print out the messages within, like this:
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% else %}
No messages.
{% endif %}
{% endwith %}