validate_on_submit is not working using WTF Forms - validation

When I registered on the page, I see that are the values getting inserted into the database and it is redirecting me to the Login function as mentioned in the code, but I don't understand why the login page itself is getting redirected every time I tried to login, instead, it should get redirected to the chat function.
I see that index function is working clearly
and also the values are getting inserted into database
But when I tried to login it's not getting redirected to the chat page
When I printed "login_form.validate_on_submit", it is always resulting false, so I could see that there is something wrong with the validate_on_submit function, but I'm not getting what it is.
This is my Login Function inside application.py:
#app.route('/login',methods=["GET","POST"])
def login():
login_form=LoginForm()
print(login_form)
if login_form.validate_on_submit():
user_object=User.query.filter_by(username=login_form.username.data)
login_user(user_object)
return redirect(url_for("chat"))
return render_template("login.html", form=login_form)
This is WTForm_Fields.py:
def invalid_credentials(form, field):
""" Username and password checker """
password = field.data
username = form.username.data
# Check username is invalid
user_data = User.query.filter_by(username=username).first()
print(user_data)
if user_data is None:
raise ValidationError("Username or password is incorrect")
# Check password in invalid
elif not pbkdf2_sha256.verify(password, user_data.hashed_pswd):
raise ValidationError("Username or password is incorrect")
class LoginForm(FlaskForm):
"""login form"""
username=StringField('username_label',validators=[InputRequired(message="username required")])
password=PasswordField('password_label',validators=[InputRequired(message="Password Required"),invalid_credentials])
This is login.html:
{% from 'form_helper.html' import displayField %}
{% extends 'prelogin-template.html'%}
{%block title%}Login{%endblock%}
{%block content%}
<h2>Login now!</h2>
<p>Enter your username/Password to start!!</p>
<form action="{{ url_for('login') }}",method='POST'>
{{displayField(form.username,"Username",autocomplete='off',autofocus=true)}}
{{displayField(form.password,"Password")}}
<div class="form-group">
<input type="submit" value="Login" class="btn btn-warning">
</div>
{{ form.crsf_token }}
</form>
{%endblock%}
This is prelogin-template.html:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>{%block title%}{%endblock%}-Let's Chat </title>
</head>
<body>
{% with messages=get_flashed_messages(with_categories=true) %}
{% if messages %}
Category:{{ messages[0][0] }}
{{messages[0][1]}}
{% endif %}
{% endwith %}
{%block content%}
{%endblock%}
</body>
</html>
This is form_helper.html:
{% macro displayField(fieldName,placeholderValue) %}
<div class='form-group'>
{{fieldName(class="form_control",placeholder=placeholderValue,**kwargs)}}
<ul class="formError">
{% for error in fieldName.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</div>
{% endmacro %}

The way I see it, there are no problems in your form_helper.html, prelogin-template.html, login.html and WTForm_Fields.py files. However, there is an issue in your Login Function. I think that Database query error has occurred. One suggestion is to simply add .first() at the end of the user object. flask_login sometimes does not log a user in when you provide flask_sqlalchemy.BaseQuery object as the parameter.
Try doing this in Login Function inside application.py:
#app.route('/login',methods=["GET","POST"])
def login():
login_form=LoginForm()
print(login_form)
if login_form.validate_on_submit():
user_object=User.query.filter_by(username=login_form.username.data).first() #<--add here
login_user(user_object)
return redirect(url_for("chat"))
return render_template("login.html", form=login_form)
This should smoothly log a user in.

Related

How to set each field as required for formset_factory in Django (how to validate blank forms in formsets)

I have the form in my forms.py:
class NameForm(forms.Form):
your_name = forms.CharField(label='Your name', max_length=100)
age = forms.CharField(label='Age', max_length=100)
sex = forms.CharField(label='Sex', max_length=100)
I created the formset_factory
NameFormSet = formset_factory(NameForm, extra=0)
In my views.py for the get context_data, I have:
...
def get_context_data(self, **kwargs):
context = super(APView, self).get_context_data(**kwargs)
if self.request.POST:
context['formset'] = NameFormSet()
else:
recommended = returnWebAttackResults(self.kwargs['webAttack'])
if recommended is None:
context['recommendedAP'] = False
else:
context['formset'] = NameFormSet(initial=recommended[0])
return context
....
In my template file, I have:
<form class="row" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ formset.management_form }}
{{ formset.non_form_errors }}
{% for forma in formset.forms %}
{{ forma }}
{% endfor %}
<input class="btn bg-success" type="submit" value="Update" />
</form>
My problem is that I don't get "This field is required" when I click the "Update" button. I tried setting the use_required_attribute to True, but it did not work.
Scenario:
The returnWebAttackResults function gets the initial data for the forms in the formset. So, if I have three forms, if one of the fields is blank (no user input) in any of the forms, then when I click the Update button, each field that is blank should be highlighted with the "This field is required". I can do this when I just render a regular form, but when I am using formsets, it does not work.
Is there a way to validate the forms in the formset before the request in sent?
I hit the same problem and found this in the documentation
https://docs.djangoproject.com/en/3.0/ref/forms/fields/#core-field-arguments
Widgets of required form fields have the required HTML attribute. Set the Form.use_required_attribute attribute to False to disable it. The required attribute isn’t included on forms of formsets because the browser validation may not be correct when adding and deleting formsets.
If you have an empty row (for adding an extra record) then you can't save the form until the empty row is filled in. So, if you want to say update one existing row without adding an extra row then you have a problem, you can't do it. The problem really comes from the fact that a formset is really in html terms just a single form. A workaround for that problem might be to use javascript to add rows to the formset only as needed, as in this example:
https://whoisnicoleharris.com/2015/01/06/implementing-django-formsets.html
There is probably furthermore a javascript solution to validate the form fields before you send the request, I think this may be such a solution but I haven't tried it:
https://jqueryvalidation.org/required-method/
It would make sense that a completely empty row should not be validated for required fields but just ignored altogether.
UPDATE: I got a simple solution working from the example in the above link. My template looks like this (note that no empty row is shown initially thanks to the if statement):
{% extends "base_generic.html" %}
{% load static %}
{% block content %}
<h1>Experiment Detail</h1>
<form name="test_form" method="post">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{% if form.name.value %}
<div class="link-formset">
{{ form.name }}
</div>
{% endif %}
{% endfor %}
<input type="submit" value="Save" class="button"/>
</form>
<!-- Include formset plugin - including jQuery dependency -->
<!-- <script src="{% static 'path_to/jquery-3.4.1.js' %}"></script> -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="{% static 'js/jquery_formset.js' %}"></script>
<script>
$('.link-formset').formset({
addText: 'Add',
deleteText: 'Delete'
});
</script>
{% endblock %}
In my view I have this for loop to set the use_required_attribute:
for form in formSet:
form.use_required_attribute = True
The solution seems to work OK, when I click the Add button an empty row appears and the empty 'name' field has the expected red box round the empty field and when I try to click Save I get the message 'Please fill out this field.'
the solution is, add BaseFormSet in froms.py, like this:
forms.py
from django.form import BaseFormSet
class NameForm(forms.Form):
your_name = forms.CharField(label='Your name', max_length=100, blank=False)
age = forms.CharField(label='Age', max_length=100, blank=False)
sex = forms.CharField(label='Sex', max_length=100, blank=False)
class RequiredFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
super(RequiredFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
form.empty_permitted = False
form.use_required_attribute = True
NameFormSet = formset_factory(NameForm, formset=RequiredFormSet)

Updating a SQL Alchemy list without reloading the page?

I am working on a small Flask app and the user is able to update a list of items via using a form to submit some data.
I am currently using SQL Alchemy as the ORM for my database and using the list in my template to display the items. I want to update it so that when the list is updated. The page is updated without the user having to reload the page.
I have tried this with AJAX using the below script but the update is not occurring.
$(document).ready(function(){
$('form').on('submit',function (e) {
$.ajax({
type: 'post',
url: '/todo',
data: $('#todoInput').serialize(),
success: function (q) {
console.log(q);
}
});
e.preventDefault();
});
}
My template:
{% extends "base.html" %}
{% block script %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="{{ url_for('static', filename='js/custom.js') }}"></script>
{% endblock %}
{% block content %}
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-success" role="alert">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
<div class="row justify-content-md-center">
<div class="col-md-auto">
<h1>What needs to be done today?</h1>
</div>
</div>
<div class="row justify-content-md-center">
<div class="col-md-auto">
<form action="" method="post">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.todo (id="todoInput") }}
{{ form.submit (class_="btn btn-primary btn-sm") }}
</div>
</form>
<div id="todoList">
<ul class="list-group">
{% for todo in todos %}
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ todo.body }}
<button type="button" class="close" aria-label="Close"><span aria-hidden="true">×</span></button>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endblock %}
Model:
from datetime import datetime
from app import db, login
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
todos = db.relationship('Todo', backref='owner', lazy='dynamic')
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)
#login.user_loader
def load_user(id):
return User.query.get(int(id))
class Todo(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self):
return '<Todo {}>'.format(self.body)
Route:
#app.route('/todo', methods=['GET', 'POST'])
def todo():
if current_user.is_authenticated:
form = TodoForm()
todos = current_user.todos.order_by(Todo.timestamp.desc()).all()
if form.validate_on_submit():
new_todo = Todo(body=form.todo.data, owner=current_user)
db.session.add(new_todo)
db.session.commit()
flash('Todo added')
todos = current_user.todos.order_by(Todo.timestamp.desc()).all()
return render_template('todo.html', form=form, todos=todos)
return render_template('todo.html', form=form, todos=todos)
flash('You first need to login or register')
return redirect(url_for('index'))
The todos list is what im trying to update. But it just reloads the page :/
You need to do two things. Firstly you need to be sure that your database is updated. That means that your form was submitted successfully. This can be done by submitting the form manually via an ajax call. This is more or less what you did. Did you check, whether your backend receives the form-submission and updates the database?
Secondly you need to update the html in the browser. Normally the page is refreshed automatically if your form is submitted. If you dont want a page reload, you need to add some client-side logic, to ensure that the page is updated. This can easily be done with a little bit of javascript.
Edit:
Your custom js might be missing }); at the end. Or did you just forget to post it here?

Django - ajax poll with results on the same page

I'm learning django and I need help with my app.
In a page I have a poll: I want that after a user has voted, the poll form disappears and a div #ajaxresults appears with the updated votes for each option.
I'm using an ajax call but I can't return the updated votes.
If I call directly '/polls/4/results' I can see the right list but I can't include that block on the same page of the form.
What am I missing?
urls.py
app_name = 'polls'
urlpatterns = [
path('', views.index, name='list'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
view.py
def results(request, question_id):
question = get_object_or_404(Question, pk=question_id)
#return render(request, 'polls/results.html', {'question': question})
return redirect(question.get_absolute_url())
#require_POST
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
selected_choice = question.choice_set.get(pk=request.POST['selectionId'])
selected_choice.votes += 1
selected_choice.save()
return redirect('polls:results', question_id = question_id)
detail template (extends base.html)
<form id="quiz-module" action="#">
<input type="hidden" id="pollId" name="pollId" value="{{question.id}}">
{% csrf_token %}
<fieldset>
<h2>{{ question.question_text }}</h2>
<ul>
{% for choice in question.choice_set.all %}
<li><input type="radio" name="opt" value="{{ choice.id }}" {% if forloop.first %}required {%endif%}/>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
</fieldset>
</form>
<section id="quiz-results">
<h3>Your vote</h3>
<p id="ajaxresults"></p>
<h3>All votes</h3>
<dl>
{%block updated_results %}{% endblock %}
</dl>
</section>
template vote is empty
template results (extends nothing)
{%block updated_results %}
{% for choice in question.choice_set.all %}
<dt>{{ choice.choice_text }}: </dt><dd id="choiceId-{{ choice.id }}">{{ choice.votes }}</dd>
{% endfor %}
{% endblock %}
js
var args = {
type:"POST",
url:"/polls/"+pollId+"/vote/",
data:data,
success: function( data ) {
//print your vote
results.children('#ajaxresults').html(selectionText);
form.hide('fast');
results.show('fast');
},
error: function(xhr, status, error) {
alert(error+'<br/>'+"Sorry. Can't submit your vote. Please, reload the page and try again")
},
};
$.ajax(args);

Can't get all comments to reload without page refresh after comment submitted thourgh ajax

I have been trying to come up with a scheme where the comments sections refreshes in a template when a user posts comments with the comment that was just posted being included. The page must not be refreshed.
Sorry for indentation.
template -
{% extends "home/header.html" %}
{% block content %}
{% if request.user.is_authenticated %}
<form>
{% csrf_token %}
<p>Comment: </p><input type="text" name="fname" id="posted_comment">
<input type="hidden" class='meme_char_id' meme_char_id={{meme.meme_char_id}}>
<input type="submit" id ="btnSubmit" name="submit" value="Post">
</form>
<!-- displaying comments here -->
<div class="box" id="comments_div">
<h3 class="h4">{{comment_count}} comments </h4>
<ul class="list-group">
{% for comment in all_comments %}
<h5>{{ comment.commentby.username }} : </h5>
<li class="list-group-item list-group-item-success">{{ comment.comment_text }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endblock %}
Now, my JS file is -
$(document).ready(function() {
$('#btnSubmit').click(function(e) {
e.preventDefault();
var data = new FormData();
var comment = $('#posted_comment').val()
var meme_char_id = $(".meme_char_id").attr('meme_char_id')
data.append('comment', comment);
data.append('meme_char_id', meme_char_id);
$.ajax({
type: 'POST',
url: '<mydomain>/comment/',
data: data,
processData: false,
contentType: false,
success: function(reply) {
$(".comments_div").html(reply)
}
})
});
});
And finally my view is -
def comment(request):
if request.is_ajax():
comment = request.POST['comment']
meme_char_id = request.POST['meme_char_id']
this_meme = Memes.objects.get(meme_char_id=meme_char_id)
print "comment - ", comment, " meme_char_id - ", meme_char_id
new_comment = Comments(comment_text=comment, meme_id=this_meme.meme_id, commentby_id=request.user.id)
new_comment.save()
all_comments = Comments.objects.filter(meme_id=this_meme.meme_id).order_by('-created_at').values()
return HttpResponse(all_comments, content_type='application/json')
else:
raise Http404
Couple questions -
I want to return an Comments query set through ajax(don't see a point in jsonifying it, if I can just send the query set to the template.)
The queryset I am returning to template isn't picked up by the comments_div in the view.
I am a rookie so please explain step by step what I am doing wrong. Thanks.
You are sending back a JSON in a response and doing$(".comments_div").html(reply). You must be sending back a template rendered with all_comments as response on successful comment. Also, a queryset is not JSON serializable as far as I understand.

Error while passing a POST data to form wizard

I have an initial form as below
forms.py
class LoginForm(forms.Form):
activity_no = forms.CharField()
username = forms.CharField()
views.py
def test(request):
if request.method == 'POST' :
form = LoginForm(request.POST)
if form.is_valid() :
act_no = form.cleaned_data['activity_no']
username = form.cleaned_data['username']
form_data = {}
form_data['activity_no'] = act_no
form_data['username'] = username
return render_to_response("test1.html", { 'form_data' : form_data}, context_instance=RequestContext(request))
else:
form = LoginForm()
return render_to_response("test.html", {'form': form }, context_instance=RequestContext(request))
test.html
{% extends "admin/base.html" %}
{% block content %}
<form action="/todo/test/" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock %}
test1.html
{% extends "admin/base.html" %}
{% block content %}
<form action="/todo/login/" method="post">{% csrf_token %}
{% for name, data in form_data.items %}
<input type="hidden" name={{name}} value={{data}}>
{% endfor %}
<input type="submit" value="Yes"/>
</form>
{% endblock %}
I fill this form, display few contents of a file and pass the above form data to a form wizard
But when I pass the data to the form wizard via post method in the template, I get a "ManagementForm Data missing" error but if the data is passed through the GET method, I don't get any error but as defined, the data is seen in the url in the GET method ( in my case it contains username which I don't want to disclose)
My Form Wizard
class LoginWizard(SessionWizardView):
def __name__(self):
"""When using decorators, Django tries to get the name of the
function and since we're a class, we'll fail. So add this method to
compensate."""
return 'LoginWizard'
template_name = "wizard_form.html"
def done(self, form_list, **kwargs) :
form_data = process_form_data(form_list)
My query is that how would I handle the post data in the form wizard.
Please let me know if I am missing anything or any other information is required from my end.
You need to add {{ wizard.management_form }} in your template as explained it in reference django wizard templates.
Like:
{% extends "admin/base.html" %}
{% block content %}
<form action="/todo/test/" method="post">{% csrf_token %}
{{ wizard.management_form }}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock %}
Add that in all templates for wizard.

Resources