Celery/Flask/Redis - apply_async function doesn't work - Updated 2 - flask

everyone.
app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0'
app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0'
celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)
def upload():
if checkDouble == None:
do_async_upload.apply_async(args=[checkDouble], countdown=15)
return 'Saved ' + file.filename
else:
return "File exists"
#celery.task
def do_async_upload(pic):
print("hello2")
db.session.add(pic)
db.session.commit()
return 0
But do_sync_upload doesn't work (hello2 doesn't print), it just skips. Redis server is working correctly
Updated:
if checkDouble == None:
print('Hello')
async_result = do_async_upload.delay(checkDouble)
print(async_result)
result = async_result.get()
return 'Saved ' + file.filename
else:
return "File exists"
But do_async_upload.apply_async doesn't seem to execute at all.
Maybe there is a problem in my Redis? Server is working, but after I try to submit something it turns from 0 to 4 clients connected.
Or even I have a problem with my datetime settings.
UPDATE 2:
I tried 2 different examples with Flask and Celery and none of them work. Redis server shows some activity, but no.
Both of them are using the same function:
Code from https://github.com/ro6ley/flask-celery-demo:
#client.task
def send_mail(data):
with app.app_context():
msg = Message("Ping!",
sender="admin.ping",
recipients=[data['email']])
msg.body = data['message']
mail.send(msg)
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
return render_template('index.html')
elif request.method == 'POST':
data = {}
data['email'] = request.form['email']
data['first_name'] = request.form['first_name']
data['last_name'] = request.form['last_name']
data['message'] = request.form['message']
duration = int(request.form['duration'])
duration_unit = request.form['duration_unit']
print("1")
send_mail.apply_async(args=[data], countdown=duration)
print("2")
flash(f"Email will be sent to {data['email']} in {request.form['duration']} {duration_unit}")
It shows flash message, but message isn't coming. Config.py is set correctly.
Next application:
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
return render_template('index.html', email=session.get('email', ''))
email = request.form['email']
session['email'] = email
# send the email
email_data = {
'subject': 'Hello from Flask',
'to': email,
'body': 'This is a test email sent from a background Celery task.'
}
if request.form['submit'] == 'Send':
# send right away
send_async_email.delay(email_data)
flash('Sending email to {0}'.format(email))
else:
# send in one minute
send_async_email.apply_async(args=[email_data], countdown=60)
flash('An email will be sent to {0} in one minute'.format(email))
return redirect(url_for('index'))
#celery.task
def send_async_email(email_data):
"""Background task to send an email with Flask-Mail."""
msg = Message(email_data['subject'],
sender=app.config['MAIL_DEFAULT_SENDER'],
recipients=[email_data['to']])
msg.body = email_data['body']
with app.app_context():
mail.send(msg)
None of the options work.

apply_async() returns an AsyncResult object IMMEDIATELY so no wonder you do not see anything and think do_async_upload() did not execute...
So here are two things I suggest you do:
1) Modify the upload() to have something like:
async_result = do_async_upload.apply_async(args=[checkDouble], countdown=15)
# .get() blocks until the task is done and returns
result = async_result.get()
2) Do not call print() in Celery tasks. Use the get_task_logger() function to obtain Celery's logger object and use it in your tasks. There is a whole section in the documentation about it.

Related

"Missing cookie \"access_token_cookie\""

guys. I have been searching all over the internet but I could not get what is the problem with my code. I am actually a Frontend developer. However, I am trying to learn Python Flask for backend part of my project.
Here is the code for login endpoint and another one which can be accessed only after we are logged in.
#app.route("/login", methods=['GET', 'POST'])
def login():
if request.method == 'POST':
phone_number = request.form.get("phone_number")
password = request.form.get("password")
user = Auth.query.filter_by(phone_number=phone_number).first()
if user and check_password_hash(user.password, password):
userlogin = UserLogin().create(user)
rm = True if request.form.get('remainme') else False
login_user(user, remember=rm)
access_token = create_access_token(identity=phone_number)
response = jsonify(access_token=access_token, role=user.role)
set_access_cookies(response, access_token)
response.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000')
response.headers['Access-Control-Allow-Credentials'] = 'true'
return response
else:
return jsonify(status=False), 401
return redirect(url_for("all_tours"))
#app.route("/admin/all_tours")
#jwt_required()
def all_tours():
all_tours = Tour.query.filter_by(owner=get_jwt_identity()).all()
response = jsonify(all_tours=[i.serialize for i in all_tours])
response.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000')
return response
When I try to send request to use all_tours endpoint, I get
msg: "Missing cookie \"access_token_cookie\""
Have no idea what is wrong.
I even tried to add these lines into def all_tours():
access_token = request.headers['Authorization'].replace("Bearer ", "")
set_access_cookies(response, access_token)
But still useless. Could you please help me to get an idea how to fix it, please?

How to get clean urls?

from flask import Flask, redirect, url_for, session, request, jsonify
from flask_oauthlib.client import OAuth
app = Flask(__name__)
app.config['GOOGLE_ID'] = "12"
app.config['GOOGLE_SECRET'] = "A"BC
app.debug = True
app.secret_key = 'development'
oauth = OAuth(app)
google = oauth.remote_app(
'google',
consumer_key=app.config.get('GOOGLE_ID'),
consumer_secret=app.config.get('GOOGLE_SECRET'),
request_token_params={
'scope': 'email'
},
base_url='https://www.googleapis.com/oauth2/v1/',
request_token_url=None,
access_token_method='POST',
access_token_url='https://accounts.google.com/o/oauth2/token',
authorize_url='https://accounts.google.com/o/oauth2/auth',
)
#app.route('/')
def index():
if 'google_token' in session:
me = google.get('userinfo')
return jsonify({"data": me.data})
return redirect(url_for('login'))
#app.route('/login')
def login():
return google.authorize(callback=url_for('authorized', _external=True))
#app.route('/logout')
def logout():
session.pop('google_token', None)
return redirect(url_for('index'))
#app.route('/login/authorized')
def authorized():
resp = google.authorized_response()
if resp is None:
return 'Access denied: reason=%s error=%s' % (
request.args['error_reason'],
request.args['error_description']
)
session['google_token'] = (resp['access_token'], '')
me = google.get('userinfo')
return jsonify({"data": me.data})
#google.tokengetter
def get_google_oauth_token():
return session.get('google_token')
Here when i am logging via google, my URL changes to something like this:
http://localhost:5000/login/authorized?code=4/U89v8kn76_zspiZUuZwdv01KuifACegxtt7NWBQLF3w#
What I want is what I gave in the URL
http://localhost:5000/login/authorized
What should I do?
This sounds like expected behavior for the callback portion of the auth process.
What you want to do is redirect the user to the main route at the end of the authorized() function. that function more or less "belongs" to the OAuth process (is a good way to think about it). you just determine if the process was successful and then redirect the user where they need to go.
i like to use Message Flashing to communicate with the user during this process.
example:
#app.route('/')
def index():
if 'google_token' not in session:
flash("Please log in to see this page")
return redirect(url_for('login'))
me = google.get('userinfo')
return render_template("index.html", user=me)
#app.route('/login/authorized')
def authorized():
resp = google.authorized_response()
if resp is None:
flash("Access denied: reason={0} error={1}".format(
request.args['error_reason'],
request.args['error_description']
))
return redirect(url_for("login"))
session['google_token'] = (resp['access_token'], '')
flash("Successful login!") # superfluous, just for example
return redirect(url_for("index"))
and you should see here that the session key is present (e.g. the cyrptocookie)... also obviously you should set secret key with os.urandom(24) per the docs

Trying to do some asynchronous processing in django on heroku using Redis Queue

I am trying to upload bigger files to s3 asynchronously.I have followed documentaion from http://python-rq.org/ . Worker is running but it is not doing doing the job.
views.py
def youmaterial(request):
if request.method == 'POST':
newdoc = Youmaterial(docfile = request.FILES['file'],user=request.user)
newdoc.save()
msg='dee'
# Redirect to the document list after POST
return HttpResponse(json.dumps({'message': msg}))
else:
form1 = DocumentForm()
return render(request,'mat_upload.html',{'form':form1})
def create_job():
redis_conn = Redis()
q = Queue(connection=redis_conn) # no args implies the default queue
# Delay execution of count_words_at_url('http://nvie.com')
job = q.enqueue(youmaterial, 'http://heroku.com')
worker.py
import os
import urlparse
from redis import Redis
from rq import Worker, Queue, Connection
listen = ['high', 'default', 'low']
redis_url = os.getenv('REDISTOGO_URL')
if not redis_url:
raise RuntimeError('Set up Redis To Go first.')
urlparse.uses_netloc.append('redis')
url = urlparse.urlparse(redis_url)
conn = Redis(host=url.hostname, port=url.port, db=0, password=url.password)
if __name__ == '__main__':
with Connection(conn):
worker = Worker(map(Queue, listen))
worker.work()
Procfie:
web: gunicorn gettingstarted.wsgi --log-file -
worker: python -u worker.py
Settings.py(This gives me error.)
DJANGO_SETTINGS_MODULE=config.settings rqworker high default low
AFAIK, you cannot enqueue the whole view function this way (expects a request object whereas you pass a str).
Are you trying to achieve some kind of async processing view? Instead you should identify the long processing function and enqueue the job inside the view, more like this:
def s3_async_upload(obj):
obj.save()
def youmaterial(request):
if request.method == 'POST':
newdoc = Youmaterial(docfile=request.FILES['file'], user=request.user)
# Replace newdoc.save()
q.enqueue(s3_async_upload, obj=newdoc)
msg = 'dee'
# Redirect to the document list after POST
return HttpResponse(json.dumps({'message': msg}))
else:
form1 = DocumentForm()
return render(request, 'mat_upload.html', {'form': form1})
# or with the `#job` decorator, usage: s3_async_upload(obj=newdoc)
#job
def s3_async_upload(obj):
obj.save()
Maybe other settings will be missing, but hope that's help!

Django ClearableFileInput widget doesn't set FileField to None

My Django app is saving articles and includes a upload file field that uses the ClearableFileInput widget When I set an article to publish, that's calling a task to send emails.
It seems to work, but there is a bug. When I edit an existing article, and mark the "clear" checkbox, the send email task fails. The error says IOError: [Errno 21] Is a directory: u'/path/to/my/static-dir'
In my edit_article view, I was trying this logic:
def edit_article(request, slug):
article = get_object_or_404(Article, slug=slug)
if request.method == 'POST':
form = ArticleForm(request.POST, request.FILES, instance=article)
if form.is_valid():
article = form.save()
msg = "Article updated successfully"
messages.success(request, msg, fail_silently=True)
if article.is_published and article.publish_date <= datetime.datetime.today():
subject = article.title
email_text = article.email_text
story_text = article.text
if article.docfile != None:
attachment = article.docfile
send_published_article.delay(request.user.email,
subject,
email_text,
story_text,
attachment)
else:
send_published_article.delay(request.user.email,
subject,
email_text,
story_text)
msg = "Article published successfully"
messages.success(request, msg, fail_silently=True)
return redirect(article)
else:
form = ArticleForm(instance=article)
return render_to_response('story/article_form.html',
{
'form': form,
'article': article,
},
context_instance=RequestContext(request))
The offending line is probably
if article.docfile != None:
But what should I use to check if the file has been cleared? What does Django's ClearableFileInput widget actually set my filefield's value to, so I can test if there's a file to attach?
Here's my tasks.py:
class EMail(object):
"""
A wrapper around Django's EmailMultiAlternatives
that renders txt and html templates.
Example Usage:
>>> email = Email(to='oz#example.com', subject='A great non-spammy email!')
>>> ctx = {'username': 'Oz Katz'}
>>> email.text('templates/email.txt', ctx)
>>> email.html('templates/email.html', ctx) # Optional
>>> email.send()
>>>
"""
def __init__(self, subject, to, cc, bcc):
self.subject = subject
self.to = to
self.cc = cc
self.bcc = bcc
self._html = None
self._text = None
self._attachment = None
def _render(self, template, context):
return render_to_string(template, context)
def html(self, template, context):
self._html = self._render(template, context)
def text(self, template, context):
self._text = self._render(template, context)
def add_attachment(self, attachment):
self._attachment = default_storage.open(attachment.name, 'r')
def send(self, from_addr=None, fail_silently=False):
if isinstance(self.to, basestring):
self.to = [self.to]
if not from_addr:
from_addr = getattr(settings, 'DEFAULT_FROM_EMAIL')
msg = EmailMultiAlternatives(
self.subject,
self._text,
from_addr,
to=self.to,
cc=self.cc,
bcc=self.bcc
)
if self._html:
msg.attach_alternative(self._html, 'text/html')
if self._attachment:
msg.attach(self._attachment.name, self._attachment.read())
msg.send()
#task(name='send-email')
def send_published_article(sender, subject, email_text, story_text, attachment=None):
"""
Task for emailing published articles.
Runs when an article is saved and is_published==True
"""
recipients = []
reporters = []
for profile in UserProfile.objects.all():
if profile.user_type == 'Client':
recipients.append(profile.user.email)
if profile.user_type == 'Reporter':
reporters.append(profile.user.email)
email = EMail(subject, to='nns.aroberts#gmail.com', cc=reporters, bcc=recipients)
ctx = {'story_text': story_text, 'email_text': email_text}
email.text('../templates/templated_email/emaila.txt', ctx)
email.html('../templates/templated_email/emaila.html', ctx) # Optional
if attachment != None:
email.add_attachment(attachment) # Optional
email.send()
I should just withdraw this question, the answer was me confusing None with Empty.
I changed
if article.docfile != None:
to
if article.docfile:
and all was right with the world.

Problem with pymongo and django unique value

I am writing django app that as a beckend is using mongodb. I am curently writing register part. Here is how I connecto to database in settings.py
if socket.gethostname() == "Production server":
CON = Connection()
DB = CON.fish
else:
CON = Connection()
DB = CON.test
DB.user.ensure_index([("username", ASCENDING),("email",ASCENDING)],unique = True)#,drop_dups=True
Here is mye register view:
def register(request):
"""
handle user registration
code variable is for testing purposes
"""
if request.method== 'GET':
form = RegisterForm(auto_id=False)
code = 1
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
elif request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
password = form.cleaned_data['password']
password_confirmation = form.cleaned_data['password_confirmation']
if password == password_confirmation:
login = form.cleaned_data['login']
email = form.cleaned_data['email']
newsletter = form.cleaned_data['newsletter']
key = register_user(login,email,password,newsletter)
if key:
#send email
send_mail("Dziękujemy za rejestrację"," Klucz aktywacyjny to " + key,settings.EMAIL_HOST_USER,[email])
request.session['email'] = email
return redirect(register_success)
else:
code = 4
error = "Login/email taken"
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
else:
code = 3
error = "invalid password"
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
else:
code = 2
return render_to_response('register_home.html',locals(),context_instance=RequestContext(request))
Here is my function I use to register user:
def register_user(login,email,password,newsletter):
"""
This function will return activation key for this user if user was added successfully or none otherwise
"""
key = generate_activation_key()
user = {
"username":login,
"email":email,
"password":crypt_password(password),
"date_join": datetime.now(),
"key": key
}
if newsletter:
user['newsletter'] = True
try:
settings.DB.user.insert(user,safe = True)
except DuplicateKeyError, error:
logging.debug("error raise during saving user")
return None
except OperationFailure, error:
logging.critical("Cannot save to database")
logging.critical(error)
else:
#we have no errors users is registred
return key
And when I test it in the browser it seems to be working. But I write test for it and it isn't working anymore. Here is code for test:
def test_valid_credentials(self):
#now try to register valid user
data = {'login':'test','password':'zaq12wsx','password_confirmation':'zaq12wsx','terms':True,'newsletter':True,'email':'test#test.com'}
response = self.c.post(reverse('register'),data)
#our user should be registred
self.assertEquals(302, response.status_code,'We dont have benn redirected')
self.assertEqual(len(mail.outbox), 1,'No activation email was sent')
#clen email box
mail.outbox = []
#now try to add another user with the same data
response = self.c.post(reverse('register'),data)
#template should be rendered with error message about used login and email
self.assertEquals(200, response.status_code)#this fails
And here is error that i get.
self.assertEquals(200, response.status_code)
AssertionError: 200 != 302
So user was registred with the same username and email which shoudn't happen. Any sugestions? Thanks in advance
Why don't you use https://github.com/django-mongodb-engine/mongodb-engine it works almost perfect with Django ORM. Works like a charm for me.