I am new to flask and tried to run this simple code from GeeksForGeeks
form.html
<form action="{{ url_for("gfg")}}" method="post">
<label for="firstname">First Name:</label>
<input type="text" id="firstname" name="fname" placeholder="firstname">
<label for="lastname">Last Name:</label>
<input type="text" id="lastname" name="lname" placeholder="lastname">
<button type="submit">Login</button>
flask_text.py
# importing Flask and other modules
from flask import Flask, request, render_template
# Flask constructor
app = Flask(__name__)
# A decorator used to tell the application
# which URL is associated function
#app.route('/', methods =["GET", "POST"])
def gfg():
if request.method == "POST":
# getting input with name = fname in HTML form
first_name = request.form.get("fname")
# getting input with name = lname in HTML form
last_name = request.form.get("lname")
return "Your name is "+first_name + last_name
return render_template("form.html")
if __name__=='__main__':
app.run()
https://www.geeksforgeeks.org/retrieving-html-from-data-using-flask/
You can find the code in the above link
I added app.debug = True but got no error log
I have my html file in /templates folder
First, The indentation of your code is wrong. Try this:
# importing Flask and other modules
from flask import Flask, request, render_template
# Flask constructor
app = Flask(__name__)
# A decorator used to tell the application
# which URL is associated function
#app.route('/', methods =["GET", "POST"])
def gfg():
if request.method == "POST":
# getting input with name = fname in HTML form
first_name = request.form.get("fname")
# getting input with name = lname in HTML form
last_name = request.form.get("lname")
return "Your name is "+first_name + last_name
return render_template("form.html")
if __name__=='__main__':
app.run()
Second, is your virtual environment running? (Can you see '(env)' at the start of your command line?)
If not, you need to first execute:
source env/bin/activate
and then pip3 install flask
If it still does not work, give me some more context.
Related
I have the code below in my Python script:
def cmd_wui(argv, path_to_tx):
"""Run a web UI."""
from flask import Flask, flash, jsonify, render_template, request
import webbrowser
app = Flask(__name__)
#app.route('/tx/index/')
def index():
"""Load start page where you select your project folder
or load history projects from local DB."""
from txclib import get_version
txc_version = get_version()
prj = project.Project(path_to_tx)
# Let's create a resource list from our config file
res_list = []
prev_proj = ''
for idx, res in enumerate(prj.get_resource_list()):
hostname = prj.get_resource_host(res)
username, password = prj.getset_host_credentials(hostname)
return render_template('init.html', txc_version=txc_version, username=username)
Also, I have an HTML form in init.html:
<form>
<input type="text" id="projectFilepath" size="40" placeholder="Spot your project files">
<input type="button" id="spotButton" value="Spot">
</form>
How can I pass the user input from "projectFilepath" when a user clicks "spotButton" on a variable in my python script?
I'm new in Python and Flask, so forgive me if I make any mistakes.
The form tag needs some attributes set:
action: The URL that the form data is sent to on submit. Generate it with url_for. It can be omitted if the same URL handles showing the form and processing the data.
method="post": Submits the data as form data with the POST method. If not given, or explicitly set to get, the data is submitted in the query string (request.args) with the GET method instead.
enctype="multipart/form-data": When the form contains file inputs, it must have this encoding set, otherwise the files will not be uploaded and Flask won't see them.
The input tag needs a name parameter.
Add a view to handle the submitted data, which is in request.form under the same key as the input's name. Any file inputs will be in request.files.
#app.route('/handle_data', methods=['POST'])
def handle_data():
projectpath = request.form['projectFilepath']
# your code
# return a response
Set the form's action to that view's URL using url_for:
<form action="{{ url_for('handle_data') }}" method="post">
<input type="text" name="projectFilepath">
<input type="submit">
</form>
You need a Flask view that will receive POST data and an HTML form that will send it.
from flask import request
#app.route('/addRegion', methods=['POST'])
def addRegion():
...
return (request.form['projectFilePath'])
<form action="{{ url_for('addRegion') }}" method="post">
Project file path: <input type="text" name="projectFilePath"><br>
<input type="submit" value="Submit">
</form>
I am trying to set up authentication with flask and ldap3. For this I have a global_authentication.py, a main.py in which I create Flask and perform the view, as well as a loginform.py where the template for the login is located, a login.html and a _formhelper.html. When I start the Flask server a login screen appears with the login data. When I enter login info I get the following error message:
ldap3.core.exceptions.LDAPPasswordIsMandatoryError: password is mandatory in simple bind
Furthermore it refers to the lines:
File "D:\Projects\admin_tool\Logic\main.py", line 49, in index
login_msg = global_ldap_authentication(login_id, login_password)
and file
"D:\Projects\admin_tool\Logic\views\global_ldap_authentication.py", line 34, in global_ldap_authentication if not connection.bind():
When i print the password, the password seems empty, and has the following content:
Password: None
cn=None,dc=example,dc=org
What I have tried so far:
Instead of: form = LoginValidation() --> LoginValidation(request.POST)
When I do this I can no longer log in and get instant the following error:
AttributeError: 'Request' object has no attribute 'POST'
I am now running out of ideas what the error could be. Can anyone help me?
main.py
from flask import Flask, render_template, request, redirect, url_for, flash, g, session
import os
from flask_bootstrap import Bootstrap
from flask_wtf.csrf import CSRFProtect
from views.global_ldap_authentication import *
from forms.LoginForm import *
from flask_cors import CORS, cross_origin
from flask.sessions import SecureCookieSessionInterface
from itsdangerous import URLSafeTimedSerializer
from flask_restx import Namespace,Resource,fields
import requests
import json
app = Flask(__name__,template_folder='templates')
bootstrap = Bootstrap(app)
app.secret_key = os.urandom(24)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
app.config['WTF_CSRF_ENABLED'] = False
#app.route('/login', methods=['GET','POST'])
def index():
# initiate the form..
form = LoginValidation()
if request.method in ('POST') :
login_id = form.user_name_pid.data
login_password = form.user_pid_Password.data
# create a directory to hold the Logs
login_msg = global_ldap_authentication(login_id, login_password)
# validate the connection
if login_msg == "Success":
success_message = f"*** Authentication Success "
return render_template('success.html', success_message=success_message)
else:
error_message = f"*** Authentication Failed - {login_msg}"
return render_template("error.html", error_message=str(error_message))
return render_template('login.html', form=form)
#app.route('/hello')
class HelloResource(Resource):
def get(self):
return {"message":"Hello World"}
if __name__ == '__main__':
app.run(debug=True)
global_ldap_authentication.py
from ldap3 import Server, Connection, ALL, SUBTREE from ldap3.core.exceptions import LDAPException, LDAPBindError
def global_ldap_authentication(user_name, user_pwd):
"""
Function: global_ldap_authentication
Purpose: Make a connection to encrypted LDAP server.
:params: ** Mandatory Positional Parameters
1. user_name - LDAP user Name
2. user_pwd - LDAP User Password
:return: None
"""
# fetch the username and password
ldap_user_name = user_name
ldap_user_pwd = user_pwd
# ldap server hostname and port
ldsp_server = f"ldap://localhost:389"
# dn
root_dn = "dc=example,dc=org"
# user
user = f'cn={ldap_user_name},{root_dn}'
print(user)
server = Server(ldsp_server, get_info=ALL)
connection = Connection(server,
user=user,
password=ldap_user_pwd)
if not connection.bind():
print(f" *** Cannot bind to ldap server: {connection.last_error} ")
l_success_msg = f' ** Failed Authentication: {connection.last_error}'
else:
print(f" *** Successful bind to ldap server")
l_success_msg = 'Success'
return l_success_msg
LoginForm.py
from main import app
from flask_wtf import Form
from wtforms import StringField, PasswordField, validators
class LoginValidation(Form):
user_name_pid = StringField('', [validators.DataRequired()],
render_kw={'autofocus': True, 'placeholder': 'Enter User'})
user_pid_Password = PasswordField('', [validators.DataRequired()],
render_kw={'autofocus': True, 'placeholder': 'Enter your login Password'})
login.html
{% from "_formhelpers.html" import render_field %}
<form method=post>
<dl>
{{ render_field(form.user_name_pid) }}
{{ render_field(form.user_pid_Password) }}
</dl>
<p><input type=submit value=Login>
</form>
_formhelpers.html
{% macro render_field(field) %}
<dt>{{ field.label }}
<dd>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
I have an order field in my models
class Order(models.Model):
# ...
order_number = models.CharField(max_length=20,blank=True, null=True)
first_name = models.CharField(max_length=50,blank=True, null=True)
last_name = models.CharField(max_length=50,blank=True, null=True)
def full_name(self):
return f'{self.first_name} {self.last_name}'
def full_address(self):
return f'{self.address_line_1} {self.address_line_2}'
def __str__(self):
return self.order_number
based on the id of the url i have an endpoint /order/download-bill/{order.id}
So what i want in my admin panel i want the admin have a button on which he click and the url trigger which will automatically download the bill
You need to configure a link callback URL for that specific purpose just configure a template to link the button.
Step 1: Need to configure a download_pdf_button.html to add a button in the admin
{% extends 'admin/change_form.html' %}
{% block submit_buttons_bottom %}
{{ block.super }}
<div class="submit-row">
<input type="submit" value="Download PDF" name="_download_pdf">
</div>
{% endblock %}
Step 2. Configure change form Response of that Model Objects
Inside admin.py
Related Imports :
xhtml2pdf is an external Pypi Package to install it ,
pip install xhtml2pdf
Also, Configure link_callback
in the same dir where admin.py is located.
create a file link_callback.py and add this code.
import os
from django.conf import settings
from django.contrib.staticfiles import finders
def link_callback(uri, rel):
"""
Convert HTML URIs to absolute system paths so xhtml2pdf can access those
resources
"""
result = finders.find(uri)
if result:
if not isinstance(result, (list, tuple)):
result = [result]
result = list(os.path.realpath(path) for path in result)
path = result[0]
else:
sUrl = settings.STATIC_URL # Typically /static/
sRoot = settings.STATIC_ROOT # Typically /home/userX/project_static/
mUrl = settings.MEDIA_URL # Typically /media/
mRoot = settings.MEDIA_ROOT # Typically /home/userX/project_static/media/
if uri.startswith(mUrl):
path = os.path.join(mRoot, uri.replace(mUrl, ""))
elif uri.startswith(sUrl):
path = os.path.join(sRoot, uri.replace(sUrl, ""))
else:
return uri
# make sure that file exists
if not os.path.isfile(path):
raise Exception(
'media URI must start with %s or %s' % (sUrl, mUrl)
)
return path
Import them.
from django.template.loader import get_template
from django.http import HttpResponse
from xhtml2pdf import pisa # import python module
from .link_callback import link_callback
Need to configure Model Admin
class OrderAdmin(admin.ModelAdmin):
# ...
change_form_template = "download_pdf_button.html" # That will be the button that can be seen in the end of change form.
def response_change(self, request, obj):
template_path = 'order_pdf.html' # template to be rendered in pdf
context = {
'order_number ': obj.order_number,
# add more things for the template if needed
}
# Create a Django response object, and specify content_type as pdf
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="OrderPDF.pdf"'
# find the template and render it.
template = get_template(template_path)
html = template.render(context) # close output file
# create a pdf
pisa_status = pisa.CreatePDF(
html, dest=response, link_callback=link_callback)
# if error then show some response
if pisa_status.err:
return HttpResponse('We had some errors <pre>' + html + '</pre>')
return response
Step: 3 Configure the Download Template for the order
In this example, I used it as order_pdf.html
<!DOCTYPE html>
<html>
<head>
<title>Order PDF</title>
</head>
<body>
<h1>{{ order_number }}</h1>
</body>
</html>
Now you will have a button at the end of the change form that will give a pdf file on click
Using a similar way you can configure it anywhere in django. If you don't need a button just want to plug inside a view. then you can easily reproduce that using that approach.
I try build gooogle login with flask login.
I find the thread here
Using Google OAuth2 with Flask
I was able to port the accepted answer to use Requests-OAuthlib instead of Rauth. As of this writing, the package's last commit was on June 2019 and was currently use by 30K+ repositories.
the init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
import dir
from flask_mail import Mail
# init SQLAlchemy so we can use it later in our models
db = SQLAlchemy()
mail = Mail()
GOOGLE_LOGIN_CLIENT_ID = "mygoogle API id .apps.googleusercontent.com" #I am sure It is correct
GOOGLE_LOGIN_CLIENT_SECRET = "my_secret_key"
def create_app():
app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'secret-key-goes-here'
# app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////' + str(dir.dir) + '/admin.sqlite'
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USERNAME'] = 'ruaxe.sdafsafsafs'
app.config['MAIL_PASSWORD'] = 'uiykejafsaffiqklziccld'
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = True
app.config['OAUTH_CREDENTIALS'] = {
'google': {
'id': GOOGLE_LOGIN_CLIENT_ID,
'secret': GOOGLE_LOGIN_CLIENT_SECRET
}
}
mail.init_app(app)
db.init_app(app)
with app.app_context():
db.create_all()
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
from .models import User
#login_manager.user_loader
def load_user(user_id):
# since the user_id is just the primary key of our user table, use it in the query for the user
return User.query.get(int(user_id))
# blueprint for auth routes in our app
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint)
# blueprint for non-auth parts of app
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
return app
Then i build the auth.py with login page
auth = Blueprint('auth', __name__)
#auth.route('/login', methods=['GET'])
def login():
if current_user is not None and current_user.is_authenticated:
return redirect(url_for('main.index'))
return render_template('login.html')
the user view page i build in main.py
from __future__ import print_function
from flask import Blueprint, render_template, request, redirect, flash, url_for, current_app
from flask_login import login_required, current_user,login_user
from project.function import cmsHaravan as hara, cmsCalendar as cal
from .function import config as cf, cmsContacts as ct
from datetime import datetime, timedelta, date
from .models import User, Order, Shop
from . import db, mail
from flask_mail import Message
import random
import string
from werkzeug.security import generate_password_hash
import json
from requests_oauthlib import OAuth2Session
from urllib.request import urlopen
main = Blueprint('main', __name__)
class OAuthSignIn(object):
providers = None
def __init__(self, provider_name):
self.provider_name = provider_name
credentials = current_app.config['OAUTH_CREDENTIALS'][provider_name]
self.consumer_id = credentials['id']
self.consumer_secret = credentials['secret']
def authorize(self):
pass
def callback(self):
pass
def get_callback_url(self):
return url_for('main.profile', provider=self.provider_name,
_external=True)
#classmethod
def get_provider(self, provider_name):
if self.providers is None:
self.providers={}
for provider_class in self.__subclasses__():
provider = provider_class()
self.providers[provider.provider_name] = provider
return self.providers[provider_name]
class GoogleSignIn(OAuthSignIn):
openid_url = "https://accounts.google.com/.well-known/openid-configuration"
def __init__(self):
super(GoogleSignIn, self).__init__("google")
self.openid_config = json.load(urlopen(self.openid_url))
self.session = OAuth2Session(
client_id=self.consumer_id,
redirect_uri=self.get_callback_url(),
scope=self.openid_config["scopes_supported"]
)
def authorize(self):
auth_url, _ = self.session.authorization_url(
self.openid_config["authorization_endpoint"])
print(auth_url)
return redirect(auth_url)
def callback(self):
if "code" not in request.args:
return None, None
self.session.fetch_token(
token_url=self.openid_config["token_endpoint"],
code=request.args["code"],
client_secret=self.consumer_secret,
)
me = self.session.get(self.openid_config["userinfo_endpoint"]).json()
print(me)
print(me["name"], me["email"])
return me["name"], me["email"]
#main.route('/authorize/<provider>')
def oauth_authorize(provider):
# Flask-Login function
if not current_user.is_anonymous:
return redirect(url_for('index'))
oauth = OAuthSignIn.get_provider(provider)
return oauth.authorize()
#main.route('/profile/<provider>')
def oauth_callback(provider):
if not current_user.is_anonymous:
return redirect(url_for('main.index'))
oauth = OAuthSignIn.get_provider(provider)
name, email = oauth.callback()
print("da lay duoc email", email)
if email is None:
# I need a valid email address for my user identification
flash('Authentication failed.')
return redirect(url_for('auth.login'))
# Look if the user already exists
user=User.query.filter_by(email=email).first()
if not user:
# Create the user. Try and use their name returned by Google,
# but if it is not set, split the email address at the #.
name = name
if name is None or name == "":
name = email.split('#')[0]
# We can do more work here to ensure a unique nickname, if you
# require that.
user=User(firstname=name, email=email)
db.session.add(user)
db.session.commit()
# Log in the user, by default remembering them for their next visit
# unless they log out.
login_user(user, remember=True)
return redirect(url_for('main.index'))
#main.route('/profile')
#login_required
def profile():
ListCarBrands = cal.getListCarBrands()
ListProvinces = cal.getListProvinces()
order_email = current_user.email
list_order = {}
i = 0
for row in Order.query.filter_by(order_email=order_email):
i = i + 1
total_order = i
list_order[i] = row.__dict__
return render_template('profile.html', list_order=list_order, name=current_user.lastname,
biensoxe=current_user.biensoxe,
ListCarBrands=ListCarBrands, brands=current_user.brands, carclass=current_user.carclass,
firstname=current_user.firstname, lastname=current_user.lastname, phone=current_user.phone,
email=current_user.email, ListProvinces=ListProvinces, provinces=current_user.provinces)
The login.html
{% extends "base.html" %}
{% block content %}
<div id="sign-in">
<h1>Sign In</h1>
<p>
<a href={{ url_for('main.oauth_authorize', provider='google') }}><img src="{{ url_for('static', filename='img/sign-in-with-google.png') }}" /></a>
</div>
<form method="POST" action="/login">
<div class="ui-information-body">
<div class="align-content-center">
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="notification is-danger">
{{ messages[0] }}
</div>
{% endif %}
{% endwith %}
<h4>Login {{ error }}</h4>
<div class="row">
<div class="col-5 "><label class="label-input-group">Email </label><input type="email"
class="next-input"
name="email" id="email"
value=""></div>
</div>
<div class="row">
<div class="col-5 "><label class="label-input-group">Password</label><input type="password"
class="next-input"
name="password"
id="password"
value=""></div>
</div>
<div class="row">
<div class="col-5 "><span class="label-input-group"><br></span>
<a href="{{ url_for('main.reset_password') }}" class="btn btn-danger btn-lg btn-block">
Nếu bạn quên mật khẩu? Reset
</a></div>
</div>
<div class="row">
<div class="col-5 "><span class="label-input-group"><br></span><input
class="btn btn-primary btn-lg btn-block" type="submit" value="Login"></div>
</div>
</div>
</div>
</div>
</form>
{% endblock %}
Profile html
{% extends "base.html" %}
{% block content %}
<form method="POST" action="/profile">
Some code here for user can update profile
</form>
</div>
{% endblock %}
Then i run program, i can get the link as the code in
def authorize(self):
auth_url, _ = self.session.authorization_url(
self.openid_config["authorization_endpoint"])
print(auth_url) ##### This function run
return redirect(auth_url)
https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=880298757050-ij79mostsm1fccdcvuj43m0oe0quisih.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fprofile%3Fprovider%3Dgoogle&scope=openid+email+profile&state=xwbrUEpMjhIFrM6l3PlXgcXdgzyDbd
127.0.0.1 - - [21/Aug/2020 14:03:32] "GET /authorize/google HTTP/1.1" 302 -
127.0.0.1 - - [21/Aug/2020 14:03:35] "GET /profile?provider=google&state=xwbrUEpMjhIFrM6l3PlXgcXdgzyDbd&code=4%2F3QFTG6I2FzBPUKD_Sk0hq4IUhlr0jA4EQ2fTLyQizyYsPkCLxRf_WXwQz929v4wUeJhN4IXWFWu7nLKBJ2NHhog&scope=email+profile+openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&authuser=0&prompt=consent HTTP/1.1" 302 -
127.0.0.1 - - [21/Aug/2020 14:03:35] "GET /login?next=%2Fprofile%3Fprovider%3Dgoogle%26state%3DxwbrUEpMjhIFrM6l3PlXgcXdgzyDbd%26code%3D4%252F3QFTG6I2FzBPUKD_Sk0hq4IUhlr0jA4EQ2fTLyQizyYsPkCLxRf_WXwQz929v4wUeJhN4IXWFWu7nLKBJ2NHhog%26scope%3Demail%2Bprofile%2Bopenid%2Bhttps%253A%252F%252Fwww.googleapis.com%252Fauth%252Fuserinfo.email%2Bhttps%253A%252F%252Fwww.googleapis.com%252Fauth%252Fuserinfo.profile%26authuser%3D0%26prompt%3Dconsent HTTP/1.1" 200 -
127.0.0.1 - - [21/Aug/2020 14:03:35] "GET /static/img/sign-in-with-google.png HTTP/1.1" 404 -
127.0.0.1 - - [21/Aug/2020 14:03:35] "GET /static/css/bootstrap.js HTTP/1.1" 404 -
BUT I TRY TO PRINT EMAIL AS THE CODE in route profile
oauth = OAuthSignIn.get_provider(provider)
name, email = oauth.callback()
print("da lay duoc email", email)
Nothing come! So anyone can help me
Thanks to all
Thanks all
I change scope and it run!
class GoogleSignIn(OAuthSignIn):
openid_url = "https://accounts.google.com/.well-known/openid-configuration"
def __init__(self):
super(GoogleSignIn, self).__init__("google")
self.openid_config = json.load(urlopen(self.openid_url))
self.session = OAuth2Session(
client_id=self.consumer_id,
redirect_uri=self.get_callback_url(),
scope='https://www.googleapis.com/auth/userinfo.profile openid https://www.googleapis.com/auth/userinfo.email'
)
I'm struggling to get Selenium working with my Django project. I can (finally) get it to get pages during testing, but I'm unable to get it to login for some reason.
This is my (very simple) test case:
import pytest
from django.conf import settings
from django.contrib.auth import get_user_model
from django.test.client import Client
from pytest_django.live_server_helper import LiveServer
from selenium.webdriver import Remote
from users.tests.factories import UserFactory
pytestmark = pytest.mark.django_db
class TestDashboard:
def test_site_loads(self, browser: Remote, test_server: LiveServer):
browser.get(test_server.url)
assert 'Welcome' in browser.title
def test_valid_login(self, browser: Remote, test_server: LiveServer, user: settings.AUTH_USER_MODEL):
password = 'testpassword'
user.set_password(password)
user.save()
browser.get(test_server.url + '/accounts/login/')
browser.find_element_by_name('login').send_keys(user.email)
browser.find_element_by_name('password').send_keys(password)
browser.find_element_by_css_selector('button[type="submit"]').click()
browser.implicitly_wait(2)
assert f'Successfully signed in as {user.username}' in browser.page_source
test_site_loads passes, test_valid_login fails, and if I example browser.current_url it's still pointing at /accounts/login/. If I look at the page source using browser.page_source, I can see "The e-mail address and/or password you specified are not correct." in the login form's error list.
I have no idea why the login credentials are failing here and I can't think of anything else to look at.
Here's what I see in the browser.page_source (captured via a print just before the assert statement) I mentioned above:
<form action="/accounts/login/" class="login auth-form" method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="xLRQ8nZlfocyyQDlJxgLL0PUvsYLLNYNHEZ5awn8cLseQoR2XNQm4TiKOgMvcaS9">
<div class="alert alert-block alert-danger">
<ul>
<li>The e-mail address and/or password you specified are not correct.</li>
</ul>
</div>
<div id="div_id_login" class="form-group">
<div class="">
<input type="email" name="login" value="ricardosoto#gmail.com" placeholder="E-mail address" autofocus="autofocus" class="textinput textInput form-control" required="" id="id_login">
</div>
</div>
<div id="div_id_password" class="form-group">
<div class="">
<input type="password" name="password" placeholder="Password" class="textinput textInput form-control" required="" id="id_password">
</div>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign In</button>
</form>
I've also include the relevant fixtures for the tests:
import environ
import pytest
import socket
from django.conf import settings
from selenium.webdriver import Remote
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from pytest_django.live_server_helper import LiveServer
from users.tests.factories import UserFactory
env = environ.Env()
#pytest.fixture
def browser() -> Remote:
driver = Remote(
command_executor=env('SELENIUM_HOST', default='http://selenium:4444/wd/hub'),
desired_capabilities=DesiredCapabilities.FIREFOX
)
yield driver
driver.quit()
#pytest.fixture
def test_server() -> LiveServer:
addr = socket.gethostbyname(socket.gethostname())
server = LiveServer(addr)
yield server
server.stop()
#pytest.fixture
def user() -> settings.AUTH_USER_MODEL:
return UserFactory()
Just as a sanity check, I've tried adding a couple of extra print statements to examine everything just prior to the assert:
print('User email: ' + user.email)
print('User is active? ' + str(user.is_active))
print('Password valid? ' + str(user.check_password(password)))
print('URL: ' +browser.current_url)
Gives me:
User email: rparsons#yahoo.com
User is active? True
Password valid? True
URL: http://172.19.0.6:44025/accounts/login/
I've also tried adding an extra test that bypasses Selnium, to try and isolate the issue a bit more:
class TestAccountLoginView:
def test_valid(self, client, user: settings.AUTH_USER_MODEL, request_factory: RequestFactory):
password = 'password'
user.set_password(password)
user.save()
response = client.post('/accounts/login/', {'login': user.email, 'password': password})
assert response.status_code == 302
This works as expected and logs me in just fine.
Update,
Looks like this may actually have something to do with pytest-django's LiveServer. Ignoring the live_server fixture, and using Django's StaticLiveServerTestCase, I can login just fine:
class TestDashboard(StaticLiveServerTestCase):
#classmethod
def setUpClass(cls):
cls.host = socket.gethostbyname(socket.gethostname())
super().setUpClass()
cls.selenium = Remote(
command_executor='http://selenium:4444/wd/hub',
desired_capabilities=DesiredCapabilities.FIREFOX
)
cls.selenium.implicitly_wait(10)
#classmethod
def tearDownClass(cls):
cls.selenium.quit()
super().tearDownClass()
def test_login(self):
get_user_model().objects.create_user(username='testuser', email='test#example.com', password='password')
self.selenium.get('%s%s' % (self.live_server_url, '/accounts/login/'))
username_input = self.selenium.find_element_by_name("login")
username_input.send_keys('test#example.com')
password_input = self.selenium.find_element_by_name("password")
password_input.send_keys('password')
self.selenium.find_element_by_xpath('//button[#type="submit"]').click()
assert 'Successful' in self.selenium.page_source
Not quite sure why the pytest-django's LiveServer is doing this, but at least I've got somewhere to look now.