Everything works fine and I get the Succes message i have set when the email is sent. But its just not sending email to the given address.
Also I get the following message in terminal when i submit the form, where the local server is running. I believe the 200 message means that the communication is successful.
"POST /submit/ HTTP/1.1" 200 10141
Given below my views.py, settings.py, forms.py and the template file.
views.py
from django.shortcuts import render_to_response
from django.template import RequestContext
from .forms import SubmitForm
from django.template import Context, loader
from django.core.mail import send_mail,EmailMessage
def submit(request):
success = False
success_message = " Thanks!"
form = SubmitForm(request.POST or None)
if form.is_valid():
original_title = request.POST.get('original_title')
english_title = request.POST.get('english_title')
sent_email = 'receive#email.com'
message_template = loader.get_template('submit.txt')
message_context = Context({ 'original_title':original_title,'english_title':english_title,
})
mail = EmailMessage(original_title, english_title, [sent_email])
mail.send()
success = True
return render_to_response('submit.html',
{'form' : form,'success': success,'success_message':success_message},
context_instance=RequestContext(request))
settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'sender#gmail.com'
EMAIL_HOST_PASSWORD = '*****'
EMAIL_PORT = 587
DEFAULT_FROM_EMAIL = 'Sample < example#gmail.com >
forms.py
from django import forms
from django.conf import settings
from django.core.cache import cache
from django.contrib.admin import widgets
from django.shortcuts import get_object_or_404
import os
class SubmitForm(forms.Form):
title = forms.CharField()
name = forms.CharField()
submit.html
<form method="post" action="{% url 'submit' %}" style="width:75%;margin:auto">{%csrf_token%}
<legend>Title</legend>
<br/>
<div class="row">
<dl class="field">
<dd><label>Title: *</label></dd>
<dt class="text">{{ form.name.errors }}
{{ form.original_title }}</dt>
<dd class="msg">You filled this out wrong.</dd>
</dl>
<dl class="field ">
<dd><label>English Title: *</label></dd>
<dt class="text">{{ form.name.errors }}
{{ form.english_title }}</dt>
<dd class="msg">You filled this out wrong.</dd>
</dl>
<input class="submit three columns" type="submit" value="Register"/>
I have added the submit.txt also in proper way. I get the success_message when i click submit but no email sent.
When you initialize an EmailMessage, the third argument is the from address. You appear to have a list containing the receiver's address instead.
Make sure you create your message in the same way as the docs:
email = EmailMessage('Hello', 'Body goes here', 'from#example.com',
['to1#example.com', 'to2#example.com'], ['bcc#example.com'],
headers = {'Reply-To': 'another#example.com'})
Related
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 %}
This code previously worked and outputed what I wanted on the website, but then this error happened
from django.shortcuts import render
import json
def get_html_content(fplid):
import requests
API_KEY = "eb9f22abb3158b83c5b1b7f03c325c65"
url = 'https://fantasy.premierleague.com/api/entry/{fplid}/event/30/picks/'
payload = {'api_key': API_KEY, 'url': url}
for _ in range(3):
try:
response = requests.get('http://api.scraperapi.com/', params= payload)
if response.status_code in [200, 404]:
break
except requests.exceptions.ConnectionError:
response = ''
#userdata = json.loads(response.text)
return response.text
def home(request):
if 'fplid' in request.GET:
fplid = request.GET.get('fplid')
html = get_html_content(fplid)
return render(request, 'scrape/home.html', {'fpldata': html})
here is my views.py file. I think I assigned html before, but I'm not sure, how is it referenced before it renders. I added scraperapi for many ip addresses, as I thought maybe I was banned from the api. I am unsure what is going on.
<body>
<h1>Enter Your FPL id </h1>
<form method="GET">
<label for="fplid"> </label>
<input type="text", name="fplid", id="fplid"> <br>
<input type="submit" value="Submit" />
</form>
<h3> {{fpldata}}</h3>
</body>
This is a part of the home.html file if it is relevant
When you initially load the page there probably wont'be an initialized ?fplid=xx. When this isn't present the variable is not assigned a value.
You could initialize the variable with html = None or this:
def home(request):
if 'fplid' in request.GET: # <- when this isnt true
fplid = request.GET.get('fplid')
html = get_html_content(fplid)
return render(request, 'scrape/home.html', {'fpldata': html})
return render(request, 'scrape/home.html')
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'
)
As you can tell, i'm new to Django, but already love it. I have a functional scraping server that scrapes 7 selects from a diagnostics page on one server. All environments I want to use this will have many of these servers with the same data to monitor performance. I would like to use this function to scrape this data from all entered target servers by input from the html template. Adding each server to monitor that will show up below the input text field from the main html page. I have completed this with a static url, but have been unsuccessful passing the different urls to scrape from the html template to the views url variable I have for the static address.
I've attempted to create forms and pass that to the html template without success, including editing the views file. Reverted the code back to the original working code to not cause more confusion.
html template:
<form method="POST">
{% csrf_token %}
<div class="field has-addons">
<div class="control is-expanded">
<input type="text" class="input"
placeholder="Relay Name">
</div>
<div class="control">
<button type="submit" class="button is-info">
Add Relay
</button>
</div>
</div>
</form>
Views.py:
import requests, bs4
from django.shortcuts import render
from django.http import HttpResponse
from bs4 import BeautifulSoup
from urllib.request import urlopen
from .models import Relay
def index(request):
url = 'hardcoded server url'
page = urlopen(url)
soup = BeautifulSoup(page, 'html.parser')
relay = 'Relay'
dic = requests.get(url.format(relay))
elema = soup.select('body > div:nth-child(13) > div.forminput')
elem1 = elema[0].text.strip()
elemb = soup.select('body > div:nth-child(14) > div.forminput')
elem2 = elemb[0].text.strip()
elemc = soup.select('body > div:nth-child(15) > div.forminput')
elem3 = elemc[0].text.strip()
elemd = soup.select('body > div:nth-child(16) > div.forminput')
elem4 = elemd[0].text.strip()
eleme = soup.select('body > div:nth-child(17) > div.forminput')
elem5 = eleme[0].text.strip()
elemf = soup.select('body > div:nth-child(18) > div.forminput')
elem6 = elemf[0].text.strip()
elemg = soup.select('body > div.versioninfo')
elem7 = elemg[0].text.strip()
#creating dictionary object
dic = {}
dic['relay'] = relay
dic['FFSL'] = elem1
dic['FFCL'] = elem2
dic['FBFQFSL'] = elem3
dic['FBQFCL'] = elem4
dic['TQQ'] = elem5
dic['SQQ'] = elem6
dic['RV'] = elem7
print(dic)
context = {'dic' : dic}
return render(request, 'relchchk/relchck.html', context)
forms.py:
from django import forms
from django.forms import ModelForm, TextInput
from .models import Relay
class RelayForm(ModelForm):
class Meta:
model = Relay
fields = ['Name', 'Relay Version', ]
widgets = {'name' : TextInput(attrs={'class' : 'input',
'placeholder' : 'url'})}
models.py:
from django.db import models
class Relay(models.Model):
name = models.CharField(max_length=45)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'Relays'
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index),
]
The desired result would be to manually enter any of the target servers that could accumulate to all and save in database that exists (but not important now) and have the main page show all selected. I was moving along pretty well and thought this should be simple step and probably is, but I must be missing something. Any guidance would be much appreciated.
You haven't created an instance of your form. In your views.py file, it should look something like this.
if request.method="POST":
#code to handle the form
else:
form = RelayForm()
context = {
"form": form
}
You render the form like this:
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
</form>
Read the docs to learn more about form handling and rendering.
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.