Sending mail using flask + blueprint - flask

I have a question about the structure of blueprint
My flask structure looks like
app/
main/
__init__.py
mail.py
__init.py
manage.py
config.py
I register blueprint in __init__.py, for app/__init__.py
from flask_mail import Mail
from flask import Flask
mail = Mail()
def create_app(config_name='develop'):
app = Flask(__name__)
from config import cfg # load EMAIL config from config.py
app.config.from_object(cfg[config_name])
cfg[config_name].init_app(app)
from .main import main # register blueprint
app.register_blueprint(main)
mail.init_app(app) # load related mail config???
return app
Put configs in config.py
class Config():
MAIL_SERVER='<My e-mail SMTP>'
MAIL_DEFAULT_SENDER=('TOPIC', 'mailID')
MAIL_USERNAME='<EMail>'
MAIL_PASSWORD='<EMail Password>'
It returns smtplib.SMTPSenderRefused error when I write codes like this in app/main/mail.py
#main.route('/mail')
def sendmail():
receivers = ['<Receiver 1>', '<Receiver 2>']
app = current_app._get_current_object() # <class 'flask.app.Flask>
mail = Mail(app)
with mail.connect() as conn:
for receiver in receivers:
msg = Message(
subject='subject test',
recipients=[receiver],
html='<h1>Hi~~</h1>')
mail.send(msg)
return 'ok'
It raise a 553 error
smtplib.SMTPSenderRefused: (553, b'Domain name required.', '=?utf-8?q?<TOPIC>?= <mailID>')
I did load config in app/__init__.py, but why I couldn't find MAIL_SERVER and related config in app/main/mail.py?
But if I reload config again in app/main/mail.py, it sends mail successfully
app.config.update(
MAIL_SERVER='<SMTP>',
MAIL_DEFAULT_SENDER=('<TOPIC>', 'mailID'),
MAIL_USERNAME='<email>',
MAIL_PASSWORD='<PASSWORD>'
)
I don't know why I have to do it twice

You should use app_context() like this:
from flask import current_app
from flask_mail import Message, Mail
with current_app.app_context():
mail = Mail()
mail.send(msg)
more info https://flask.palletsprojects.com/en/1.1.x/extensiondev/

The following code is render HTML template for sending mail body including styles or formatted HTML.
from <project-module> import mail
token = user.get_reset_token()
msg = Message('Password Reset Request', sender='<sender-mail>', recipients=[user.email])
msg.html = render_template('email_templates/password_reset.html',
home_link=url_for('home.index', _external=True),
reset_link=url_for(
'account.reset_token', token=token, _external=True),
name=user.name,
email=user.email)
mail.send(msg)
As you mentioned above code. I have been twice initialize Mail object, which is un-necessary.
#main.route('/mail')
def sendmail():
receivers = ['<Receiver 1>', '<Receiver 2>']
mail = Mail()
with mail.connect() as conn:
for receiver in receivers:
msg = Message(
subject='subject test',
recipients=[receiver],
html='<h1>Hi~~</h1>')
mail.send(msg)
return 'ok'

Not a direct answer to your question, but actually I don't think you need to initialize the mail twice.
If you would initialize the mail in app/main/mail.py, there's no point having mail.init_app(app) in app/__init__.py because you never imported it.
Otherwise, in app/main/mail.py I would do import app.mail without creating another mail so that you won't have this config issue in the first place.

Related

FLASK, How to redirect Google autentication to a local html.file

Hi everyone I have implemented the Google Authentication with API.
I would like that once the user is authenticated the page redirect to a local html/Javascript application.
I am trying the following code which is not working because it is not finding the correct url.
from flask import Flask, redirect, url_for, session, request, render_template
from authlib.integrations.flask_client import OAuth
import os
from datetime import timedelta
# App config
app = Flask(__name__)
app.secret_key = "random secret"
# oAuth Setup
oauth = OAuth(app)
google = oauth.register(
name='google',
client_id='',
client_secret='',
access_token_url='https://accounts.google.com/o/oauth2/token',
access_token_params=None,
authorize_url='https://accounts.google.com/o/oauth2/auth',
authorize_params=None,
api_base_url='https://www.googleapis.com/oauth2/v1/',
client_kwargs={'scope': 'openid email profile'},
)
#app.route('/')
def hello_world():
email = dict(session).get('email',None)
return f'Hello, {email}!'
#app.route('/login')
def login():
google = oauth.create_client('google')
redirect_uri = url_for('authorize', _external=True)
return google.authorize_redirect(redirect_uri)
#app.route('/authorize')
def authorize():
google = oauth.create_client('google')
token = google.authorize_access_token()
resp = google.get('userinfo')
user_info = resp.json()
session['email'] = user_info['email']
return redirect('file:///home/luca/Desktop/ft_userdata/ProvaLogIn/prova.htm')
if __name__ == "__main__":
app.run(debug=True)
Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
THe html that i am trying to render is simply an html local file with wrote hello
you need to create folder called templates inside your project in this folder you will add your html file in your case it will be prova.html
and change the return statement to
return render_template('prova.html')
this how should your directory tree should look like (general view)
├── app.py
├── static
└── templates
└── status.html
hope this solve your issue

Using global variable with application context in flask

I defined a customized logger in mylogger.py, and the log dir is defined is config.py as LOG_DIR='/var/log'. In mylogger.py, the logger does some init work with LOG_DIR.
//mylogger.py
from flask import current_app
log_path = os.path.join(current_app.config['LOG_DIR'],"all.log")
handler = logging.FileHandler(filename = logPath)
....
//config.py
LOG_DIR = "/var/log"
Flask says an error message:
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with modules.app_context(). See the
documentation for more information.
How could I use variable in config.py within app_context? Thank you.
def register_logging(app):
log_path = os.path.join(app.config['LOG_DIR'],"all.log")
handler = logging.FileHandler(filename = logPath)
def create_app():
app = Flask()
register_logging(app)
return app
app = create_app()
or just import config.py
from config import LOG_DIR

Send all emails to specific email address instead for development in django

I would like to redirect all emails normally sent with send_mail() to me(and only me) when I work locally on the project.
I'm aware that I could use the file backend or console backend to see the emails but I need to be able to open the attached files so I can inspect them. Is there any way to do this easily?
Thanks!
Let's suposse you have a variable with the destination email.
destination_email = ...
In your settings add this:
# settings.py
DEBUG = True
...
DEBUG_DESTINATION_EMAIL = 'youremail#yourdomain.com'
So, where you send the email:
from django.conf import settings
...
if settings.DEBUG:
destination_email = settings.DEBUG_DESTINATION_EMAIL
else:
destination_email = ... # get the destination email normally
You could try writing your own custom email backend. You could subclass the smtp backend and change the recipients address before the email is sent.
Try django-email-bandit
Quoted from their readme.
To install django-email-bandit via pip:
pip install django-email-bandit
Add django-email-bandit to your installed apps:
INSTALLED_APPS = (
...
'bandit',
...
)
For your test environment you should enable the backend:
EMAIL_BACKEND = 'bandit.backends.smtp.HijackSMTPBackend'
and set the email which will receive all of the emails:
BANDIT_EMAIL = 'bandit#example.com'
I'm just writing simple email backend to send all emails to my debug email address.
import smtplib
from django.conf import settings
from django.core.mail.backends.smtp import EmailBackend
class TestEmailBackend(EmailBackend):
def _send(self, email_message):
message = email_message.message()
try:
self.connection.sendmail(settings.DEFAULT_FROM_EMAIL, settings.TEST_EMAIL_ADDRESS, message.as_bytes(linesep='\r\n'))
except smtplib.SMTPException:
if not self.fail_silently:
raise
return False
return True
You should set your email in TEST_EMAIL_ADDRESS variable in your settings.py and set EMAIL_BACKEND to 'path.to.your.module.TestEmailBackend'

celery with flask: application not registered on db instance and no application bound to current context

I'm trying to add some background tasks to my flask app using celery. I'm using blueprint with my flask app like this:
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config.from_object('config')
db.init_app(app)
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
from .api_1_0 import api as api_1_0_blueprint
app.register_blueprint(api_1_0_blueprint, url_prefix='/api/v1.0')
return app
And I have run.py like this:
app = create_app()
app.run(port=7000, debug=True, threaded=True)
Then I have a separate file with some tasks:
celery = Celery(__name__, broker=CELERY_BROKER_URL)
#celery.task
def send_async_sms(to, msg):
app = current_app._get_current_object()
with app.app_context():
time.sleep(12)
print "Sending to {0}: {1}".format(to, msg)
Without the two lines with app context, things work fine. But adding them cause the following problem:
ERROR/MainProcess] Task app.notify.send_async_sms[0d535fc4-7465-470e-9204-548ecca2c6e0] raised unexpected: RuntimeError('working outside of application context',)
What am I doing wrong here?

Django EmailMessage not sending/timeout

im trying to use django.core.mail to send emails using the default backend and it doesn't seem to be working. I've set up the email credentials, server, and port number in the settings file but whenever I try to run the send() method of an email message the command hangs indefinitely.
views.py
from django.core.mail import send_mail
def sending_email(request):
message = ""
subject = ""
send_mail(subject, message, from_email, ['to_email',])
Add this in settings.py
# Sending mail
EMAIL_USE_TLS = True
EMAIL_HOST='smtp.gmail.com'
EMAIL_PORT=587
EMAIL_HOST_USER='your gmail account'
EMAIL_HOST_PASSWORD='your gmail password'
I was having the same issue when trying to send via smtp.gmail.com with use_tls=True. It turns out I had the wrong port set. Here's what I'm doing now and it works:
from django.core.mail import get_connection
from django.core.mail.message import EmailMessage
connection = get_connection(use_tls=True, host='smtp.gmail.com', port=587,username='YourEmail#gmail.com', password='YourPassword')
EmailMessage('test', 'test', 'addr#from.com', ['addr#to.com'], connection=connection).send()