Sending Confirmation Emails with Flask, RQ (Working outside of application context.) - flask

I am trying to send confirmation emails when a new user registers. When I send a confirmation email, everything works fine, however, when I try to run that task in the background I get the following error:
File "/Users/martifont/Dev/bz/./FlaskApp/utils.py", line 22, in send_confirmation_email
msg = Message()
I have tried running a different task in the background with no issues at all.
users.py
#imports
from .utils import send_reset_email, send_confirmation_email, example
users = Blueprint('users', __name__)
#SIGNUP
#users.route('/signup', methods=\['GET', 'POST'\])
def signup():
form = SignupForm()
if form.validate_on_submit():
user = User(email=form.email.data, username=form.username.data, confirmed=False, is_admin=False, password = generate_password_hash(form.password.data, method='sha256'))
db.session.add(user)
db.session.commit()
user_id=user.id
with app.app_context():
job = q.enqueue(send_confirmation_email, user)
login_user(user)
flash('A confirmation email has been sent to your inbox.', 'is-success')
return redirect(url_for('users.account', id=user.id))
return render_template('signup.html', form=form)
utils.py
def send_confirmation_email(user):
token = user.get_confirmation_token()
msg = Message()
msg.subject = ('Email Confirmation')
msg.sender = 'MAIL_DEFAULT_SENDER'
msg.recipients = [user.email]
msg.body = f'''
Welcome { user.username }!,
Thanks for signing up. Please follow this link to activate your account:
{url_for('users.confirm_email', token=token, _external=True)}
Thanks!
'''
mail.send(msg)
EDIT
After following Sebastian's advice, I think the issue is solved, however, I am still getting the following error message.
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/flask/app.py", line 2548, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/flask/app.py", line 2528, in wsgi_app
response = self.handle_exception(e)
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/flask/app.py", line 2525, in wsgi_app
response = self.full_dispatch_request()
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/flask/app.py", line 1822, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/flask/app.py", line 1820, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/flask/app.py", line 1796, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
File "/Users/martifont/Dev/bz/FlaskApp/users.py", line 30, in signup
job = q.enqueue(send_confirmation_email, args=(app, user))
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/rq/queue.py", line 514, in enqueue
return self.enqueue_call(
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/rq/queue.py", line 410, in enqueue_call
return self.enqueue_job(job, pipeline=pipeline, at_front=at_front)
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/rq/queue.py", line 572, in enqueue_job
job.save(pipeline=pipe)
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/rq/job.py", line 694, in save
mapping = self.to_dict(include_meta=include_meta)
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/rq/job.py", line 629, in to_dict
'data': zlib.compress(self.data),
File "/Users/martifont/Dev/bz/venv/lib/python3.10/site-packages/rq/job.py", line 305, in data
self._data = self.serializer.dumps(job_tuple)

When you are in a view or inside the creat_app factory function there is no need to use the app_context() context manager.
The traceback defines where the error is been emited and that is when you instantiated the msg=Message() variable so instead of calling the app context inside the view i suggest you to refactor your view and send functions like so:
users.py
from flask import current_app
#users.route('/signup', methods=\['GET', 'POST'\])
def signup():
form = SignupForm()
if form.validate_on_submit():
user = User(email=form.email.data, username=form.username.data, confirmed=False, is_admin=False, password = generate_password_hash(form.password.data, method='sha256'))
db.session.add(user)
db.session.commit()
user_id=user.id
# pass the current app object to the send_confirmation_email function.
# Its important to pass the object and no the proxy
app = current_app._get_current_object()
job = q.enqueue(send_confirmation_email, args=(app, user))
login_user(user)
flash('A confirmation email has been sent to your inbox.', 'is-success')
return redirect(url_for('users.account', id=user.id))
return render_template('signup.html', form=form)
utils.py
def send_confirmation_email(app, user):
with app.app_context():
token = user.get_confirmation_token()
msg = Message()
msg.subject = ('Email Confirmation')
msg.sender = 'MAIL_DEFAULT_SENDER'
msg.recipients = [user.email]
msg.body = f'''
Welcome { user.username }!,
Thanks for signing up. Please follow this link to activate your account:
{url_for('users.confirm_email', token=token, _external=True)}
Thanks!'''
mail.send(msg)

Related

How to fix TypeError: validate() got an unexpected keyword argument 'extra_validators'?

I've put together basic login/register forms in a flask app using WTForms. I included parts of code from two files that I thought might be relevant.
I am getting the error:
Traceback (most recent call last):
File "/home/neon/flask-app/venv/lib/python3.8/site-packages/flask/app.py", line 2548, in __call__
return self.wsgi_app(environ, start_response)
File "/home/neon/flask-app/venv/lib/python3.8/site-packages/flask/app.py", line 2528, in wsgi_app
response = self.handle_exception(e)
File "/home/neon/flask-app/venv/lib/python3.8/site-packages/flask/app.py", line 2525, in wsgi_app
response = self.full_dispatch_request()
File "/home/neon/flask-app/venv/lib/python3.8/site-packages/flask/app.py", line 1822, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/home/neon/flask-app/venv/lib/python3.8/site-packages/flask/app.py", line 1820, in full_dispatch_request
rv = self.dispatch_request()
File "/home/neon/flask-app/venv/lib/python3.8/site-packages/flask/app.py", line 1796, in dispatch_request
return self.ensure_sync(self.view_functions\[rule.endpoint\])(\*\*view_args)
File "/home/neon/flask-app/src/accounts/views.py", line 18, in register
if form.validate_on_submit():
File "/home/neon/flask-app/venv/lib/python3.8/site-packages/flask_wtf/form.py", line 86, in validate_on_submit
return self.is_submitted() and self.validate(extra_validators=extra_validators)
TypeError: validate() got an unexpected keyword argument 'extra_validators'
This is my code:
src/accounts/forms.py
from flask_wtf import FlaskForm
from wtforms import EmailField, PasswordField
from wtforms.validators import DataRequired, Email, EqualTo, Length
from src.accounts.models import User
class RegisterForm(FlaskForm):
email = EmailField(
"Email", validators=[DataRequired(), Email(message=None), Length(min=6, max=40)]
)
password = PasswordField(
"Password", validators=[DataRequired(), Length(min=6, max=25)]
)
confirm = PasswordField(
"Repeat password",
validators=[
DataRequired(),
EqualTo("password", message="Passwords must match."),
],
)
def validate(self):
initial_validation = super(RegisterForm, self).validate()
if not initial_validation:
return False
user = User.query.filter_by(email=self.email.data).first()
if user:
self.email.errors.append("Email already registered")
return False
if self.password.data != self.confirm.data:
self.password.errors.append("Passwords must match")
return False
return True
src/accounts/views.py
from flask import Blueprint, flash, redirect, render_template, request, url_for
from flask_login import login_required, login_user, logout_user, current_user
from src import bcrypt, db
from src.accounts.models import User
from .forms import LoginForm, RegisterForm
accounts_bp = Blueprint("accounts", __name__)
#accounts_bp.route("/register", methods=["GET", "POST"])
def register():
if current_user.is_authenticated:
flash("You are already registered.", "info")
return redirect(url_for("core.home"))
form = RegisterForm(request.form)
if form.validate_on_submit():
user = User(email=form.email.data, password=form.password.data)
db.session.add(user)
db.session.commit()
login_user(user)
flash("You registered and are now logged in. Welcome!", "success")
return redirect(url_for("core.home"))
return render_template("accounts/register.html", form=form)
It's throwing an error in the register function at the second conditional on form.validate_on_submit(). I looked at the WTForms documentation, and from what I could understand, the validate() function is supposed to take an argument extra_validators. So I don't understand why it would be throwing this error.
Full disclosure, I was following a tutorial, and did compare my code to their final code and it matched.
What am I missing here?
You ignore that validate takes additional parameters. If you add this in your implementation it should work.
def validate(self, extra_validators=None):
initial_validation = super(RegisterForm, self).validate(extra_validators)
# ...

runtime error. there is no current event loop in thread 'Thread-1'

i followed this tutorial https://python.gotrained.com/scraping-telegram-group-members-python-telethon/, and the code ran successfully on a single python script. so i decided to implement the same code into django views.py. form.py, html templates and urls.py are properly connected. whenever i runserver and enter details it raises runtime error saying "There is no current event loop in thread 'Thread-1'."
views.py
import asyncio
from django.shortcuts import render,redirect
from telethon.sync import TelegramClient
from .form import DetailForm,CodeForm
from .models import Detail,Code
# Create your views here.
def home(request):
form = DetailForm
context = {'form':form}
if request.method=='POST':
form = DetailForm(request.POST)
if form.is_valid():
form.save()
details = Detail.objects.all().last()
api_id = int(details.api_id)
api_hash = details.api_hash
phone_number = details.phone_number
loop = asyncio.new_event_loop()
client = TelegramClient(phone_number, api_id, api_hash, loop=loop)
client.connect()
if not client.is_user_authorized():
client.send_code_request(phone_number)
# global client, phone_number
return redirect('code')
return render(request, 'teleapp/home.html', context)
def codeview(request):
code=CodeForm
# codemodel = Code.objects.all().last()
if request.method == 'POST':
codes = CodeForm(request.POST)
if codes.is_valid():
codes.save()
#what next...
return redirect('scrapy')
context = {'code':code}
return render(request, 'teleapp/code.html', context)
def scrappy(request):
details = Detail.objects.all().last()
context = {'details':details}
return render(request, 'teleapp/scrappy.html', context)```
**error raised**
Internal Server Error: /
Traceback (most recent call last):
File "C:\Users\User\PycharmProjects\telegram\venv\lib\site-packages\django\c
ore\handlers\exception.py", line 34, in inner
response = get_response(request)
File "C:\Users\User\PycharmProjects\telegram\venv\lib\site-packages\django\c
ore\handlers\base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Users\User\PycharmProjects\telegram\venv\lib\site-packages\django\c
ore\handlers\base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\User\PycharmProjects\telegram\scrapy\teleapp\views.py", line
24, in home
client.connect()
File "C:\Users\User\PycharmProjects\telegram\venv\lib\site-packages\telethon
\sync.py", line 35, in syncified
loop = asyncio.get_event_loop()
File "C:\Users\User\AppData\Local\Programs\Python\Python36-32\lib\asyncio\ev
ents.py", line 694, in get_event_loop
return get_event_loop_policy().get_event_loop()
File "C:\Users\User\AppData\Local\Programs\Python\Python36-32\lib\asyncio\events.py", line 602, in get_event_loop
% threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'Thread-2'.

None type error in Django rest RegisterView

I am using Django RestFramework's RegisterView for the registration API in my project. I use a custom adapter as well, it is as follows.
class CustomAccountAdapter(DefaultAccountAdapter):
"""
Override all_auth's default adapter
"""
def send_confirmation_mail(self, request, emailconfirmation, signup):
# current_site = get_current_site(request)
promo_code = emailconfirmation.email_address.user.promo_code
activate_url = "%s/#/verifyEmail/%s/" % (
settings.FRONTEND_HOSTNAME,
emailconfirmation.key
)
if promo_code:
activate_url = "%s/#/verifyEmail/%s/?promo_code=%s" % (
settings.FRONTEND_HOSTNAME,
emailconfirmation.key,
promo_code
)
ctx = {
"user": emailconfirmation.email_address.user,
"activate_url": activate_url,
"current_site": settings.FRONTEND_HOSTNAME
}
if signup:
email_template = 'account/email/email_confirmation_signup'
else:
email_template = 'account/email/email_confirmation'
self.send_mail(email_template,
emailconfirmation.email_address.email,
ctx)
I get an error while the sending the confirmation mail to the newly registered user.The error is below:
File
"/home/ubuntu/fizprod_env/local/lib/python2.7/site-packages/django/core/mail/message.py",
line 283, in send
return self.get_connection(fail_silently).send_messages([self]) File
"/home/ubuntu/fizprod_env/local/lib/python2.7/site-packages/django/core/mail/backends/smtp.py",
line 99, in send_messages
sent = self._send(message) File "/home/ubuntu/fizprod_env/local/lib/python2.7/site-packages/django/core/mail/backends/smtp.py",
line 110, in _send
from_email = sanitize_address(email_message.from_email, email_message.encoding) File
"/home/ubuntu/fizprod_env/local/lib/python2.7/site-packages/django/core/mail/message.py",
line 105, in sanitize_address
nm, addr = addr TypeError: 'NoneType' object is not iterable
Can anyone tell me what is the error and how it's happening? I use Django 1.7, Python 2.7, Django REST 3.1.1.

LoopExit: 'This operation would block forever' with Redis as cache on Django

I work with Django 1.7 python 2.7. In Django I have several functions. One function prepares necessary information and stores it in Redis. After that another function asynchronously starts several times, gets information from the Redis, calculates some data and returns to the client. The function runs from browser, and I have the next errors:
user_names = json.loads(cache.get(user_id))
File "/home/user/.virtualenvs/Django/local/lib/python2.7/site-packages/django_redis/cache.py", line 25, in _decorator
return method(self, *args, **kwargs)
File "/home/user/.virtualenvs/Django/local/lib/python2.7/site-packages/django_redis/cache.py", line 73, in get
client=client)
File "/home/user/.virtualenvs/Django/local/lib/python2.7/site-packages/django_redis/client/default.py", line 215, in get
value = client.get(key)
File "/home/user/.virtualenvs/Django/local/lib/python2.7/site-packages/redis/client.py", line 863, in get
return self.execute_command('GET', name)
File "/home/user/.virtualenvs/Django/local/lib/python2.7/site-packages/redis/client.py", line 565, in execute_command
return self.parse_response(connection, command_name, **options)
File "/home/user/.virtualenvs/Django/local/lib/python2.7/site-packages/redis/client.py", line 577, in parse_response
response = connection.read_response()
File "/home/user/.virtualenvs/Django/local/lib/python2.7/site-packages/redis/connection.py", line 569, in read_response
response = self._parser.read_response()
File "/home/user/.virtualenvs/Django/local/lib/python2.7/site-packages/redis/connection.py", line 224, in read_response
response = self._buffer.readline()
File "/home/user/.virtualenvs/Django/local/lib/python2.7/site-packages/redis/connection.py", line 162, in readline
self._read_from_socket()
File "/home/user/.virtualenvs/Django/local/lib/python2.7/site-packages/redis/connection.py", line 120, in _read_from_socket
data = self._sock.recv(socket_read_size)
File "/home/user/.virtualenvs/Django/local/lib/python2.7/site-packages/gevent/_socket2.py", line 253, in recv
self._wait(self._read_event)
File "/home/user/.virtualenvs/Django/local/lib/python2.7/site-packages/gevent/_socket2.py", line 152, in _wait
self.hub.wait(watcher)
File "/home/user/.virtualenvs/Django/local/lib/python2.7/site-packages/gevent/hub.py", line 559, in wait
result = waiter.get()
File "/home/user/.virtualenvs/Django/local/lib/python2.7/site-packages/gevent/hub.py", line 800, in get
return self.hub.switch()
File "/home/user/.virtualenvs/Django/local/lib/python2.7/site-packages/gevent/hub.py", line 538, in switch
return greenlet.switch(self)
LoopExit: ('This operation would block forever', <Hub at 0x7f3b65bb0370 epoll pending=0 ref=0 fileno=27>)
Of the five times the function is executed 3-4 times, or can execute all 5 times.
UPD (add code).
My code something like that:
import json
from django.core.serializers.json import DjangoJSONEncoder
from .models import users, reports
from .helpers import FILTERS
KEYS = ['query_1', 'query_2', 'query_3', 'query_4', 'query_5']
def initial_data(request):
context = {}
user_names = users.objects.filter(**FILTERS).values_list('user_name', 'user_last_name', 'user_email')
user_names = {email: {' '.join((name, last_name)) for name, last_name, email in user_names}}
user_id = str(request.user.id)
try:
cache = get_cache('default')
cache.set(user_id, json.dumps(user_names, cls=DjangoJSONEncoder))
except:
context['errors'] = ["Can't get access to the Redis"]
else:
context['errors'] = []
return HttpResponse(json.dumps(context, cls=DjangoJSONEncoder), content_type="application/json")
def data(request):
context = {}
key = request.GET['key']
user_id = str(request.user.id)
cache = get_cache('default')
user_names = json.loads(cache.get(user_id))
if key == 'query_1':
emails = list(user_names.keys())[0:9]
if key == 'query_2':
emails = list(user_names.keys())[10:19]
if key == 'query_3':
emails = list(user_names.keys())[20:29]
if key == 'query_4':
emails = list(user_names.keys())[30:39]
if key == 'query_5':
emails = list(user_names.keys())[40:49]
result = reports.objects.filter(user_email__in=emails).values_list('date', 'text', 'user_email')
for date, text, email in result:
context[email] = [user_names[email], date, text]
return HttpResponse(json.dumps(context, cls=DjangoJSONEncoder), content_type="application/json")
In function initial_data I collect users information from database, that is necessary to filter reports and store it in Redis.
FE knows all KEYS ('query_1', 'query_2', 'query_3', 'query_4', 'query_5') and asynchronously sent request with one key at a time to the function data.

Flask-Login: KeyError: 'security' when trying to call login_user

I have the following code that logs in a user on my Flask application:
#app.route('/login', methods = ['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
db = get_db()
member = member_from_phone(request.form['phone_number'])
member_obj = Member(member)
if member is None:
g.result = json.dumps({'head' : 200, 'body' : "invalid phone" })
return 'no member...'
elif request.form['password'] == member['password']:
login_user(member_obj)
return 'login successful'
else:
return 'login failed'
return home_page()
However it throws a KeyError: 'security' when I try log in:
File "/Library/Python/2.7/site-packages/flask_security/utils.py", line 64, in login_user
if _security.trackable:
File "/Library/Python/2.7/site-packages/werkzeug/local.py", line 338, in __getattr__
return getattr(self._get_current_object(), name)
File "/Library/Python/2.7/site-packages/werkzeug/local.py", line 297, in _get_current_object
return self.__local()
File "/Library/Python/2.7/site-packages/flask_security/utils.py", line 35, in <lambda>
_security = LocalProxy(lambda: current_app.extensions['security'])
KeyError: 'security' when trying to call login_user
I have no idea what's going on, I think I might need to fix my flask configuration but I don't know how.