Difficulty with Celery: function object has no property 'delay' - python

I have been churning through the software development recently and have had some success bending celery to my will.
I have used it successfully to send emails, and have just tried to use almost exactly the same code (after restarting all processes etc) to send sms through Twilio.
However I keep getting the following issue:
File "/Users/Rob/Dropbox/Python/secTrial/views.py", line 115, in send_sms
send_sms.delay(recipients, form.text.data)
AttributeError: 'function' object has no attribute 'delay'
My code is as follows:
#celery.task
def send_email(subject, sender, recipients, text_body):
msg = Message(subject, sender=sender)
for email in recipients:
msg.add_recipient(email)
msg.body = text_body
mail.send(msg)
#celery.task
def send_sms(recipients, text_body):
for number in recipients:
print number
num = '+61' + str(number)
print num
msg = text_body + 'this message to' + num
client.messages.create(to=num, from_="+14804054823", body=msg)
send_email.delay when called from my views.py works perfectly, however send_sms.delay fails every time with the above error.
Any help on troubleshooting this is appreciated.
-- As requested:
#app.route('/send_mail', methods=['GET', 'POST'])
#roles_accepted('Admin')
def send_mail():
form = SendMailForm(request.form)
if request.method == 'POST':
if form.validate_on_submit():
emails = db.session.query(User.email).all()
list_emails = list(zip(*emails)[0])
send_email.delay('Subject', 'sender#example.com', list_emails, form.text.data)
return render_template('send_generic.html', form=form)
#app.route('/send_sms', methods=['GET', 'POST'])
#roles_accepted('Admin')
def send_sms():
form = SendMailForm(request.form)
if request.method == 'POST':
if form.validate_on_submit():
recipients = db.session.query(User.mobile).all()
list_recipients = filter(None, list(zip(*recipients)[0]))
send_sms.delay(list_recipients, form.text.data)
return render_template('send_generic.html', form=form, send_sms=send_sms)
My send_sms celery decorated function is showing up as a registered task:
(env)RP:secTrial Rob$ celery inspect registered
-> celery#RP.local: OK
* app.send_email
* app.send_security_email
* app.send_sms
and for config I am simply using the guest:rabbitmq
CELERY_BROKER_URL = 'amqp://guest#localhost//'
CELERY_RESULT_BACKEND = 'amqp://guest#localhost//'

The view name send_sms conflicts with the celery task name. The name send_sms references the view, not the task, when used in the module that contains the view.
Use different name to avoid the overwriting.

Related

Routing in Flask when no function is given

I was going through this website where no function has been given to the '/' route but it still opens the login page when not logged in, I'm not sure how exactly this routing works...
Link:
https://www.geeksforgeeks.org/profile-application-using-python-flask-and-mysql/
Code:
#app.route('/')
#app.route('/login', methods =['GET', 'POST'])
def login():
msg = ''
if request.method == 'POST' and 'username' in request.form and 'password' in request.form:
username = request.form['username']
password = request.form['password']
cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute('SELECT * FROM accounts WHERE username = % s AND password = % s', (username, password, ))
account = cursor.fetchone()
if account:
session['loggedin'] = True
session['id'] = account['id']
session['username'] = account['username']
msg = 'Logged in successfully !'
return render_template('index.html', msg = msg)
else:
msg = 'Incorrect username / password !'
return render_template('login.html', msg = msg)
Flask lets you define routes in decorators. A given function may have one or more decorators.
Since the decorator for the / route is on the same function as the login/ route, both routes are handled by the login() function.
Since it's kind of confusing to have both routes pointing to the same function, one suspects that the author of the tutorial is using the first decorator as a placeholder, and will eventually change the / route to point to a template for a "landing page". (There are lots of reasons to have multiple routes hitting the same function, but no others are apparent from the code).
You might have been better served had the author included a comment, or explicitly redirected /:
#route("/")
def placeholder():
"""Redirect root to ``login`` for now"""
return redirect("/login")
Does this make sense?

RuntimeError: working outside of request context Flask Session

I am getting RuntimeError: working outside of request context error while running a test in Flask. I've tried multiple suggestions from other threads, but none has worked for me.
Part of my views.py:
#user_app.route('/login', methods =('GET', 'POST'))
def login():
form = LoginForm()
error = None
if form.validate_on_submit():
user = User.objects.filter(username=form.username.data).first()
if user:
if bc.hashpw(form.password.data, user.password) == user.password:
session['username']=form.username.data
return 'User Logged In'
else:
user = None
if not user:
error = 'Incorrect credentials'
return render_template('user/login.html',form=form,error=error)
Relevant part of my tests.py:
from application import create_app as create_app_base
def create_app(self):
self.db_name = 'flaskbook_test'
return create_app_base(
MONGODB_SETTINGS={'DB':self.db_name},
TESTING=True,
WTF_CSRF_ENABLED=False,
SECRET_KEY='SecretKey'
)
def setUp(self):
self.app_factory = self.create_app()
self.app = self.app_factory.test_client()
#self.app.application.app_context().push() <-- this did not help
def tearDown(self):
db = _get_db()
db.client.drop_database(db)
def test_login_user(self):
#create user
self.app.post('/register', data=self.user_dict())
#login user
rv = self.app.post('/login',data=dict(
username='username',
password='password'
))
#check session is set
with self.app as c:
rv = c.get('/')
assert session.get('username') == self.user_dict()['username']
I have already tried adding app_context and self.app.application.app_context().push() as mentioned above:
with self.app.application.app_context():
assert session.get('username') == self.user_dict()['username']
But it didn't work. Whenever I call session['username'] I get RuntimeError: working outside of request context.
My requirements.txt: Flask0.10.1 Flask-Script 2.0.5 flask-mongoengine 0.7.4
Please help.
What you want is the request context, not the app context.
Flask includes some handy functions to push a request context for you - check out the Flask testing docs and you'll see a lot of relevant info, including the test_request_context method on the app object.
Combine that with app.test_client to push a request context and then simulate client behaviour such as POSTing to your endpoint. Try this:
with self.app.test_request_context('/'), self.app.test_client() as c:
rv = c.post('/')
assert session.get('username') == self.user_dict()['username']

flask mail "running out of context error" even with context

I am trying to send mail in a celery task using flask-mail, however I continue to get this runtime error RuntimeError('working outside of application context',). This is the code I have in the file itself:
from app import app
from celery import Celery
from flask.ext.mail import Message
from flask import current_app
# config
app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0'
app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0'
# set up celery
celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)
# put the processing task here
#celery.task
def send_results(filename, email_addr):
msg = Message('testing email', recipients=[email_addr])
msg.body = 'testing this funct'
with app.app_context():
current_app.mail.send(msg)
print(filename)
print(email_addr)
Note that it has the app.app_context() line in there (At this point I'm not sure if it's right or not)
Another thing to note is that the app isn't fully "completed" yet. Specifically what I mean by this is that the creation of the app is handled by a function called create_app as seen here: https://github.com/swarajd/seq2flux/blob/master/app/startup/create_app.py
That function is called in manage.py here: https://github.com/swarajd/seq2flux/blob/master/manage.py
I try to call this task in a file called views.py which handles all the views. The one function in particular that deals with this is as follows:
#core_blueprint.route('data_analysis', methods=['GET', 'POST'])
#login_required
def data_analysis():
# print(request.form)
form = DataAnalysisForm(CombinedMultiDict((request.files, request.form)))
# print(form)
if (request.method == 'POST' and form.validate()):
# print(request.form)
# print(request.files)
file = request.files['seqFile']
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
mail_engine = current_app.extensions.get('mail', None)
# print(mail_engine)
# print(current_user)
send_results.delay(filename, current_user.email)
flash('processing job scheduled!')
# Redirect to home page
return redirect(url_for('core.data_analysis'))
return render_template('core/data_analysis.html', form=form)
The important line is: send_results.delay()
My question is: even with context, why is it throwing that runtime error?
The reason this was happening was because the email MESSAGE itself was being created outside the with statement, causing the runtime error.

How to check request method inside Python class?

I have a class for Flask:
class Likes(object):
def __init__(self, model, table_id):
self.model = model
self.table_id = table_id
if request.form["likes"] == 'like':
query = self.model.query.filter_by(id=table_id).first()
query.likes += 1
db.session.commit()
flash(u'Like =)) ' + query.title, 'info')
elif request.form["likes"] == 'dislike':
query = self.model.query.filter_by(id=table_id).first()
query.likes -= 1
db.session.commit()
flash(u"Don't like =(" + query.title, 'info')
and I want to call this class every time user sent POST request, but every time I create an instance of my class I need add check request type:
# ...
if request.method == 'POST':
Likes(Post, request.form["post_id"])
# ...
How can I improve my class and add inside it this check:
if request.method == 'POST':
# ...
Solution:
Use decorator #app.before_request
#app.before_request
def before_req():
if request.method == 'POST':
flash(u'Before request', 'success')
You can use Flask.request_started signal to run something everytime a request arrive and then execute the code you require.
flask.request_started
This signal is sent before any request processing started but when the request context was set up. Because the request context is
already bound, the subscriber can access the request with the standard
global proxies such as request.
Have a look at the Flask's Signals chapter to learn more.
Use something like that in your code:
def create_like(sender, **extra):
if request.method == 'POST':
Likes(Post, request.form["post_id"])
from flask import request_started
request_started.connect(create_like, app)
This was adapted from the example for the documentation of Core Signals.

Access request parameters from flask application deployed in openshift

I have a python web app that carry's out calculations on data you send to it via POST / GET parameters.
The app works perfectly on my machine, but when deployed to openshift, it fails to access the parameters with an error no 32 : Broken pipe
I then used this quickstart repo to just focus on server code and not app code.
Got to differentiate between a POST and GET request and ended there
here's the relevant python code :
#app.route('/', methods=['GET','POST'])
def index():
result = ""
if request.method == "GET":
name = request.form['name'] if "name" in request.form else ""
result = "We received a GET request and the value for <name> is :%s" % name
elif request.method == "POST":
result = "We received a POST request"
else :
result = "We don't know what type of request we have received"
return result
So i just wanna know how i can access the parameters.
Don't use Flask's development server in production. Use a proper WSGI server that can handle concurrent requests, like Gunicorn. For now try turning on the server's threaded mode and see if it works.
app.run(host="x.x.x.x", port=1234, threaded=True)
You can get form data from the POST request via:
name = request.form.get("name")
Refactor:
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
name = request.form.get("name")
result = "We received a POST request and the value for <name> is - {0}".format(name)
else:
result = "This is a GET request"
return result
Refer to the official Flask documentation to learn more about the Request object.

Categories

Resources