I am struggling passing data to a form using pytest. I wish to pass the username and password to a login form. Here is what I have tried:
conftest.py
import pytest
from wahoo_connect import init_app, db
from wahoo_connect.models import User
from dotenv import load_dotenv
load_dotenv('.flaskenv')
#pytest.fixture(scope='module')
def app():
app = init_app()
with app.app_context():
db.create_all()
user = User(username='testuser', email='test#gmail.com', forename='Test', surname='User', confirmed=True)
user.set_password('testing')
db.session.add(user)
db.session.commit()
yield app
#pytest.fixture(scope='module')
def client(app):
return app.test_client()
this sets up a client fixture which is used by my test:
def test_index_page__logged_in(client):
with client:
formdata = {'username': 'testuser', 'password': 'testing', 'remember_me': False}
client.post('/auth/login', data=formdata)
assert current_user.username == 'testuser'
the route is defined in a blueprint as:
#auth_bp.route('/login', methods=['GET', 'POST'])
def login():
# Login route logic goes here
if current_user.is_authenticated:
return redirect(url_for('home_bp.index'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password', 'warning')
return redirect(url_for('auth_bp.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('home_bp.index')
return redirect(next_page)
return render_template('auth/login.html', title='Sign In', form=form)
and here is the form:
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()])
remember_me = BooleanField('Remember Me')
submit = SubmitField('Sign In')
and the template:
{% extends "base.html" %}
{% block content %}
<div class="form-signin text-center">
<form action="" method="post" novalidate>
<h3>Please sign in</h3>
<div class="form-floating">
{{ form.username(type="text", class="form-control", id="floatingInput", placeholder="Username") }}
{% for error in form.username.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
<label for="floatingInput">{{ form.username.label.text }}</label>
</div>
<div class="form-floating">
{{ form.password(type="password", class="form-control", id="floatingPassword", placeholder="Password") }}
{% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
<label for="floatingPassword">{{ form.password.label.text }}</label>
</div>
<div class="checkbox mb-3">
<label>
{{ form.remember_me() }} {{ form.remember_me.label.text }}
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">{{ form.submit.label.text }}</button>
{{ form.hidden_tag() }}
</form>
</div>
<p class="text-center">Forgot Your Password? Click to Reset</p>
<p class="text-center pt-3">Create an Account</p>
{% endblock %}
I can see the data before the "if form.validate_on_submit():" statement in the view and it is correct, but I can never get beyond this point in the submission process. I seem to be missing something!
Thank you
Martyn
It seems that I was missing an important flag:
WTF_CSRF_ENABLED = False
it now works!
Related
I have this app and its working but i'm confused whether to use form method or POST.get method. with form i'm getting so many challenges like rendering form on custom html
suppose i have this change password screen, for that i need to create form then use this on html template and with custom html it gets more complicated to use form fields.
forms.py:
class ChangePasswordForm(PasswordChangeForm):
old_password = forms.CharField(label="Old Password", strip=False, widget=forms.PasswordInput(
attrs={'class': 'formField password-genrInput'}))
new_password1 = forms.CharField(label="New Password", strip=False, widget=forms.PasswordInput(
attrs={'class': 'formField password-genrInput'}))
new_password2 = forms.CharField(label="Confirm Password", strip=False, widget=forms.PasswordInput(
attrs={'class': 'formField password-genrInput'}))
class Meta:
model = User
fields = ('old_password', 'new_password1', 'new_password2')
views.py:
# Password Change View
def changePassword(request):
if request.method == 'POST':
form = ChangePasswordForm(request.user, request.POST)
print(form)
if form.is_valid():
print("form valid")
user = form.save()
update_session_auth_hash(request, user)
messages.success(request, "Password Changed Successfully")
return redirect('changePassword')
else:
messages.error(request, "Something Went Wrong, Please Try Again ")
return redirect('changePassword')
else:
form = ChangePasswordForm(request.user)
return render(request, 'admin/user_auth/change_password.html', {
'form': form
})
html:
{% extends "admin/layouts/default.html" %}
{% load static %}
{% block content%}
<div class="row">
<div class="col">
<div class="titleBlock">
<h1><i class="fas fa-chevron-circle-left mr-3"></i>Back</h1>
</div>
<div class="card">
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li {% if message.tags %} class=" {{ message.tags }} " {% endif %}> {{ message }} </li>
{% endfor %}
</ul>
{% endif %}
<form method="post">
{% csrf_token %}
<div class="formBlock">
<div class="row password-genr mt-5">
{% for field in form %}
<div class="col-md-7">
<div class="formControl static ">
<label for="" class="formLabel">{{field.label}}</label>
{{field}}
</div>
</div>
{%endfor%}
<div class="col-md-7">
<div class="btnBlock mt-5">
<button type="submit" class="btn btn-md">Save</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
{%endblock content%}
but with simple method i would have checked first if new password and confirm password are matching then
old_password = request.POST.get('old_password')
new_password = request.POST.get('new_password')
check the old password are matching with db then save the new password on db.
so which method should i use and why?
TO do This you can do something like this.
from django.contrib.auth.hashers import check_password
current_password = request.user.password
old_password = request.POST.get('old_password')
matchcheck= check_password(old_password, current_password) #this returns True or False
if matchcheck:
#change your password
else:
#tell the user the password is wrong.
The method above is useful by example if a user wants to delete his account to do so you can use the idea to check whether he knows his password or not ,if he knows it then he can delete his account.
And if you do not want to implement it by yourself you can use the built-in in Django(i really recommend this method for changing password because it well done and less headache).
The post requests from the frontend do not get saved in the database, without any error shown. However, when I manually add entries from the admin panel, it shows on the frontend.
My index.html(form part):
<form class="main__input--form" method="POST">
{% csrf_token %}
<p class="main__input--text">
<textarea name="content" id="content" class="main__input--content" cols="35" rows="8" aria-label="Entry content" placeholder="Enter text here..."></textarea>
</p>
<button class="main__input--submit" type="submit">Vent</button>
</form>
My extension of index which loops through the database entries:
{% for obj in all_vents %}
<div>
<h1>{{obj.vent}}</h1>
</div>
<br />
{% endfor %}
My models.py:
class Vents(models.Model):
vent = models.CharField(max_length=10000)
def __str__(self):
return self.vent
My forms.py:
from django import forms
from .models import Vents
class VentForm(forms.ModelForm):
class Meta:
model = Vents
fields = ['vent']
My views.py:
from django.shortcuts import render, redirect
from .forms import VentForm
from .models import Vents
def ventout(request):
if request.method == "POST":
form = VentForm(request.POST or None)
if form.is_valid():
form.save()
return redirect("ventout")
else:
all_vents = Vents.objects.all()
return render(request, "ventout.html", {"all_vents": all_vents})
Views:
def ventout(request):
all_vents = Vents.objects.all()
if request.method == "POST":
form = VentForm(request.POST or None)
if form.is_valid():
form.save()
return redirect("ventout")
else:
form = VentForm()
context = {"all_vents": all_vents, "form":form}
return render(request, "ventout.html", context)
Template:
<form class="main__input--form" method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="main__input--submit">Vent</button>
</form>
you could install/use "crispy_forms_tags" to make the form look better,
https://django-crispy-forms.readthedocs.io/en/latest/index.html
if you want to go further you could install/use "widget_tweaks"
https://pypi.org/project/django-widget-tweaks/
Your index.html from part should have {{ form }} form tag, as I guess.
Try Using following code
<form class="main__input--form" method="POST">
{% csrf_token %}
{{ form }}
<p class="main__input--text">
<textarea name="content" id="content" class="main__input--content"
cols="35" rows="8" aria-label="Entry content" placeholder="Enter text here...">
</textarea>
</p>
<button class="main__input--submit" type="submit" value="Submit">Vent</button>
</form>
I have a situation where I want to have published functionality for articles, and I have it. The problem is that I can't get my unit test to work properly, trying to post True to the published field.
Here's what my tests look like.
class ArticleDetailViewTest(TestCase):
def setUp(self):
email = 'testuser#gmail.com'
first_name = 'test'
last_name = 'user'
password = 'test15146'
user = User.objects.create_user(email, first_name, last_name, password)
self.client = Client()
self.client.login(username='testuser#gmail.com', password='test15146')
def test_can_publish_article_from_POST(self):
other_article_two = Article.objects.create(name='Test Name One', author=User.objects.get(email='testuser#gmail.com'))
correct_article_two = Article.objects.create(name='Test Name Two', author=User.objects.get(email='testuser#gmail.com'))
response = self.client.post(reverse('publish_article', kwargs={'pk' : correct_article_two.pk}))
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, f'/articles/{correct_article_two.pk}/')
self.assertEqual(correct_article_two.published, True)
Here's what my publish article view looks like.
def publish_article(request, pk):
article = get_object_or_404(models.Article, pk=pk)
if request.method == "POST":
article.published = True
article.save(update_fields=['published'])
return redirect('article_detail_view', pk=article.pk)
else:
raise Http404
Here are my urls.
from django.urls import path
from . import views
urlpatterns = [
path('', views.HomePageView.as_view(), name='home'),
path('new/', views.ArticleCreateView.as_view(), name='article_new'),
path('<int:pk>/', views.ArticleDetailView.as_view(), name='article_detail_view'),
path('<int:pk>/publish/', views.publish_article, name='publish_article'),
path('<int:pk>/unpublish/', views.unpublish_article, name='unpublish_article'),]
Here are my models.
from django.conf import settings
from django.db import models
from django.urls import reverse
from django import forms
class Article(models.Model):
name = models.CharField(max_length=255, blank=False)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
published = models.BooleanField(default=False)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('article_detail_view', args=[str(self.id)])
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ['name',]
Lastly, here is the template.
{% extends 'base.html' %}
{% block title %}{{ article.name }}{% endblock %}
{% block content %}
{% if article.author_id == user.pk %}
<div class="row">
<div class="col-6 col-centered">
<h2 class="text-center">{{ article.name }}</h2>
<p class="text-center">by {{ article.author.first_name }} {{ article.author.last_name }}</p>
{% if article.published %}
<small class="form-text text-muted mb-1">This article is public.</small>
<form method="post" action="/articles/{{ article.pk }}/unpublish/">
{% csrf_token %}
<input type="submit" name="unpublish" value="Unpublish Article" id="id_unpublish_button" class="btn btn-primary btn-md"/>
</form>
{% else %}
<small class="form-text text-muted mb-1">This article is private.</small>
<form method="post" action="/articles/{{ article.pk }}/publish/">
{% csrf_token %}
<input type="submit" name="publish" value="Publish Article" id="id_publish_button" class="btn btn-primary btn-md"/>
</form>
{% endif %}
</div>
</div>
{% elif article.published %}
<div class="row">
<div class="col-6 col-centered">
<h2 class="text-center">{{ article.name }}</h2>
<p class="text-center">by {{ article.author.first_name }} {{ article.author.last_name }}</p>
</div>
</div>
{% else %}
<div class="row">
<div class="col-6 col-centered">
<p class="text-center">This article is private.</p>
</div>
</div>
{% endif %}
{% endblock %}
This is the error message I'm getting from my test. The issue seems to be I can post to the URL with self.client.post . Any help would be greatly appreciated.
FAIL: test_can_publish_article_from_POST (articles.tests.ArticleDetailViewTest)
Traceback (most recent call last):
File "/Users/christopher.chough/article_directory/articles/tests.py", line 126, in test_can_publish_article_from_POST
self.assertEqual(correct_article_two.published, True)
AssertionError: False != True
Ran 17 tests in 2.340s
FAILED (failures=1)
Object in your test method not updated. You can use refresh_from_db method to update it after changes:
def test_can_publish_article_from_POST(self):
other_article_two = Article.objects.create(name='Test Name One', author=User.objects.get(email='testuser#gmail.com'))
correct_article_two = Article.objects.create(name='Test Name Two', author=User.objects.get(email='testuser#gmail.com'))
response = self.client.post(reverse('publish_article', kwargs={'pk' : correct_article_two.pk}))
correct_article_two.refresh_from_db() # Add this line
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, f'/articles/{correct_article_two.pk}/')
self.assertEqual(correct_article_two.published, True)
I have created two different types of users - truck & company. Here is my registration page for a user
After registering, the data about whether the user is a truck or company will go to the database.
In my login page,
only Email and Password are to be entered. In my custom user creation form, I added the field username = email.
When I am trying to login with valid credentials, the page is not redirecting me to a particular page according to the user-type. Instead, an error which I created for invalid credentials in login.html is raising - "Your email and password didn't match. Please try again."
here's my code:
views.py:
def login_view(request):
title = "Login"
if request.method == 'POST':
form = LoginForm(data=request.POST)
email = request.POST.get('email', '')
password = request.POST.get('password', '')
user = auth.authenticate(username=email, password=password)
if form.is_valid():
auth.login(request, user)
user_type = form.cleaned_data['Label']
if user.is_active & user_type == 'Truck':
return HttpResponseRedirect('/post_load/')
elif user_type == 'Company':
return HttpResponseRedirect('/live_deal/')
else:
form = LoginForm()
return render(request, 'registration/login.html', {'form' : form, 'title': title})
urls.py:
# url(r'^login/$', views.login_view),
# url(r'^accounts/login/$', views.login_view),
url(r'^login/$', 'django.contrib.auth.views.login', {'authentication_form': LoginForm}),
url(r'^accounts/login/$', 'django.contrib.auth.views.login', {'authentication_form': LoginForm}),
forms.py:
class LoginForm(auth.forms.AuthenticationForm):
email = forms.EmailField(label=_("Email"),widget=forms.EmailInput)
CHOICES= (('Truck', 'Truck'),('Company', 'Company'),)
Label = forms.ChoiceField(choices=CHOICES, label='Label', widget=forms.RadioSelect())
login.html:
{%extends "registration/header.html"%}
{% block content %}
{% if form.errors %}
<p>Your email and password didn't match. Please try again.</p>
{% endif %}
<form class="form-horizontal" method="post" action = "." >{%csrf_token%}
<div class="panel panel-default login">
<div class="panel-heading">
<span class="glyphicon glyphicon-lock"></span> Login</div>
<div class="panel-body">
<form class="form-horizontal" role="form">
<div class="form-group">
<div class='col-sm-6 col-sm-offset-4'>
<table border="0">
<div class="col-sm-4">
<tr><th><label for="id_user" class="col-sm-4 control-label">Email:</label></th><td>{{ form.email }}</td></tr> </div>
<div class="col-sm-4">
<tr><th><label for="id_password" class="col-sm-4 control-label">Password:</label></th><td>{{ form.password }}</td></tr> </div>
</table> </div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<div class="checkbox">
<label>
<input type="checkbox"/>
Remember me
</label>
</div>
</div>
</div>
<div class="form-group last">
<div class="col-sm-offset-4 col-sm-8">
<button type="submit" class="btn btn-success btn-sm">
Sign in</button>
<input type="hidden" name="next" value="/" />
<label class="col-sm-offset-3">
Forget Password?
</label>
</div>
</div>
</form>
</div>
<div class="panel-footer">
Not Registered? Register</div>
</div>
</form>
{% endblock %}
{% if form.errors %}
<p>Your email and password didn't match. Please try again.</p>
This error is rather broad. You could loop through it to see the actual errors.
Better yet; don't use your own authentication system just use Django's built-in system which also allows you to add extra fields to the user model.
Example:
from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
# Redirect to a success page.
else:
# Return a 'disabled account' error message
...
else:
# Return an 'invalid login' error message.
...
Note, you don't need to write your own login view, you can also use Django's login system to handle the form, pass reset/change, urls and templates (which you can override with your own template):
urls:
urlpatterns = [
url('^', include('django.contrib.auth.urls')),
]
Then /login/ will be your login page.
After it works with the Django contrib auth module, extend it with your own custom HTML login form.
django.contrib.auth.views
I am trying to authenticate a user(using the simple authenticate() function) in django.
def auth(request):
if request.method == 'POST':
auth_form = AuthenticationForm(request.POST)
if auth_form.is_valid():
auth_form.save()
user = authenticate(username=request.POST['id_username'],password=request.POST['id_password'])
if user is not None:
login(request,user)
return redirect('/profile/home/')
else:
return redirect('/')
else:
return redirect('/')
def register(request):
if request.method == 'POST':
form = SimpleUserCreation(request.POST)
if form.is_valid():
form.save()
user = authenticate(username=request.POST['id_username'],password=request.POST['id_password1'])
login(request,user)
return redirect('/profile/home/')
else:
return redirect('/')
This is the template displaying the forms - Just wanted to display login and register forms in the same page(for this example)
{% extends 'base.html' %}
{% load bootstrap_toolkit %}
{% block content %}
<div class="row">
<div class="span4 offset1 login">
<form class="form-signin" action="/auth/" method="POST">
{% csrf_token %}
{{ auth_form|as_bootstrap }}
<br>
<center>
<button class="btn btn-large btn-primary" type="submit">
Sign In
</button>
</center>
</form>
</div>
<div class="span4 offset2 signup">
<form action="/register/" method="POST">
{% csrf_token %}
{{ form|as_bootstrap }}
<br>
<center>
<button class="btn btn-large btn-primary" type="submit">
Register
</button>
</center>
</form>
</div>
</div>
{% endblock %}
I am getting an error like this:
ValueError at /auth/
The view SimpleUserAuth.auth.views.auth didn't return an HttpResponse object.
Any idea where i am going wrong?? I think its the authenticating function's inability to find the correct id for the fields...maybe i am wrong. I am a Noob :|
Cheers
In your auth method, if auth_form.is_valid() returns False, you do not return a response object.
The same is the case in def register(request): . If it is a GET request, the method does not return a response object.
Hence the error(s)
I made the mistake in these lines -
1) AuthenticationForm takes argument as follows:
AuthenticationForm(data=request.POST)
2) u can't save AuthenticationForm.
auth_form = AuthenticationForm(request.POST)
if auth_form.is_valid():
auth_form.save()
Thanks for the help karthik :)