url_for in celery doesn't work - python

We are building a notofication mailing system as part of a python/flask system, as for now it has been run using cronjobs but we're moving everything to celery to make it more performant and easier to maintain.
However the templates which have been working so far suddendly start throwing the following error:
[2017-05-29 20:30:30,411: WARNING/PoolWorker-7] [2017-05-29 20:30:30,411]
ERROR in mails: ERROR errorString => Could not build url for endpoint
'page.start' with values ['from_email']. Did you mean 'static' instead?
The url_for is called in an external template as follows:
{{ url_for('page.start', _external=True) }}
and rendered as follows:
message = render_template('notifs/user_notif.html',
subject=subject,
entries = grouped,
user=u,
unsubscribe_hash=pw_hash,
list_id = str(notif_list.id),
timestamp = today)
Now if we rip out all the url_for in the template it works. But why?
My hypothesis, which i can't test or proof: Somehow celery does not have access to the Blueprints (even though it is running in the application context, as the tasks actually accesses all kinds of models and the db etc.). How do I make celery understand url_for?

Just ran into the same issue:
The database and models are fronted by by your ORM (Flask-sqlalchemy?), not the Flask application itself. Flask the application provides the context for things like url_for, current_user, etc. Your orm just provides the database abstraction and is not contingent on the actual application context.

Related

DRY Principles for FastAPI + Jinja2 Web App (Front and Backend)

I'm learning FastAPI by building a basic Blog. I finished the schema, CRUD, and API and I'm trying to implement a frontend with Jinja2 templates. However, as soon as I started writing the webapp front end I realized I'm literally re-writing every single "back-end" API function for the frontend. This can't possibly be correct.
For example, in order to render a html blog feed on the front end (app/webapp/article/article.py), it seems like I need to write identical functions to my app/api/api_v1/endpoints/article.py.
My API request to display all blog posts looks like this:
# DisplayArticles is a pydantic class
#router.get("/articles", response_model=DisplayArticles, tags=["articles"])
async def get_articles(
tag: str = "",
db: AsyncIOMotorClient = Depends(get_database),
):
filters = ArticleFilters(
tag=tag
)
dbarticles = await get_articles_with_filters(
db, filters
)
return create_aliased_response(
DisplayArticles(articles=dbarticles, articles_count=len(dbarticles))
)
And then I found myself essentially rewriting an identical function for my webapp, except I replaced the return statement with return templates.TemplateResponse and added a bit of context metadata.
My goal was to design a project structure that would allow the project to easily grow/scale whiteout needing to be restructured.
Here is a skeleton of my slightly simplified file structure:
app/
-api/
--api_v1/
---api/
---api.py # holds the `router.include_router(router_name)` from endpoints/*
----endpoints/
-----article.py # holds routes returning pydantic response models
-----tag.py
-core/
--config.py
--utils.py
-crud/
--article.py # all the usual CRUD type methods with DB filters etc
--tag.py
-db/
--database.py
-models/
--article.py # pydantic models
--tag.py
-templates/ # Jinja2 templates
--assets/
--base.html
-webapp/
--articles/
--base.py
-main.py

How to create a private endpoint in flask

In my flask application I need to fetch data from my database whenever the users clicks a button, I know I can just make a flask route, using the flask restful module or plain routes. But my question is if I can make that route/endpoint not visible for users.
#app.route("/api/fetch/")
def fetch_data():
return some_data
I dont wan't the user having access to this endpoint directly, I just want the web application to be able to use it. Not sure if it is possible or where to look.
I found that maybe using Flask-CORS could help. Any help or guidance would be appreciated.
On flask you can use Blueprint
example from the above snippet would be
from flask import Blueprint
sample_bp = Blueprint("sample_bp", __name__)
#sample_bp.before_request
def restrict_with_token():
# Do something here on checking header or token
#sample_bp.route("/api/fetch/")
def fetch_api():
# Your logic
Otherwise you can have some referrence here : https://flask.palletsprojects.com/en/1.1.x/tutorial/views/

Reflecting different databases in Flask factory setup

I'd like to use Flask's application factory mechanism fpr my application. I have is that the databases I use within some blueprints are located differently, so I'm using binds for pointing to them. The tables itself are in production and already in use, so I need to reflect them in order to use them within my application.
Problem is that I can't get the reflect function working because of the application context. I always get the message, that I'm working outside the application context. I fully understand that and see, that db is really outside, but don't have any idea anymore on how to involve it.
I tried different variations on passing app via current_app to my models.py, but nothing was working.
config.py:
class Config(object):
#Secret key
SECRET_KEY = 'my_very_secret_key'
ITEMS_PER_PAGE = 25
SQLALCHEMY_BINDS = {
'mysql_bind': 'mysql+mysqlconnector://localhost:3306/tmpdb'
}
SQLALCHEMY_TRACK_MODIFICATIONS = False
main.py:
from webapp import create_app
app = create_app('config.Config')
if __name__ == '__main__':
app.run(debug=true)
webapp/init.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app(config_object):
app=Flask(__name__)
app.config.from_object(config_object)
db.init_app(app)
from main import create_module as main_create_module
main_create_module(app)
return app
webapp/main/init.py:
def create_module(app):
from .controller import blueprint
app.register(blueprint)
webapp/main/controller.py:
from flask import Blueprint, render_template, current_app as app
from .models import db, MyTable # <-- Problem might be here ...
bluerint = Blueprint('main', __name__)
#blueprint.route('/'):
def index():
resp = db.session.query(MyTable)\
.db.func.count(MyTable.versions)\
.filter(MyTable.versions =! '')\
.group_by(MyTable.name).all()
if resp:
return render_template('index.html', respo=respo)
else:
return 'Nothing happend'
webapp/main/models.py:
from .. import db # <-- and here ...
db.reflect(bind='mysql_bind')
class MyTable(db.Model):
__bind_key__ = 'mysql_bind'
__table__ = db.metadata.tables['my_table']
Expected result would be to get the reflection working in different blueprints.
Got it working, full solution here:
https://github.com/researcher2/stackoverflow_56885380
I have used sqllite3 for the test, run create_db.py script to setup db. Run flask with debug.sh, since recent versions you can't seem to just app.run() inside __main__ anymore.
Explanation
As I understand it a blueprint is just a way to group together several views if you need to use them multiple times in a single app or across multiple apps. You can add different route prefix as you desire.
A db object is not associated with a blueprint, it is associated with an app, which provide the configuration information. Once inside the blueprint views you will have access to the db object with the relevant app context automatically available.
Regarding the db.reflect, you need to make the call inside create_app and pass it the app object(preferred) or import the app inside the model which is spaghetti.
Multiple DBs can be accessed using binding as you've shown.
So your blueprints will have access to all tables imported and flask-sqlalchemy knows which db connection to use based on the binding.
I'm normally a fan of explicitly defining tables so you have access to the ORM objects and fields in code completion. Do you have lots of tables/fields or maybe you are creating something to query table metadata for total automation on any schema? Like a schema viewer or something like that.
This might be useful for others coming to this post:
https://flask-sqlalchemy.palletsprojects.com/en/2.x/contexts/
Brilliant! Thank you very much. Got it also working. Your tip gave me a hint to find another way:
#blueprint.route('/')
def index():
# pushing app_context() to import MyTable
# now I can use db.reflect() also in models.py
with app.app_context():
from .models import MyTable
results = db.session.query(MyTable).all()
print(results)
for row in results:
print (row)
print(row.versions)
print(row.name)
if results:
return render_template('my_table.html', results=results)
else:
return 'Nothing happend'
Then the reflection can be done inside models.py. The link you posted is really helpful, don't know why I did not stumble over it myself ...
Anyway, I do now have a lot more possibilities than before!
Cheers, mate!

How to setup different subdomains in Flask (using blueprints)?

I have a Flask application running at https://app.mydomain.com.
The blueprints look like this:
app.register_blueprint(main)
app.register_blueprint(account, url_prefix='/account')
app.register_blueprint(users, url_prefix='/users')
app.register_blueprint(boxes, url_prefix='/boxes')
app.register_blueprint(api_1_0, url_prefix='/api/v1.0')
The URLs look like this:
https://app.mydomain.com
https://app.mydomain.com/account
https://app.mydomain.com/users
...
I want to move the api_1_0 route from https://app.mydomain.com/api/v1.0 to https://api.mydomain.com, how should I modify the routes and how should I set app.config['SERVER_NAME']?
example.com (without any subdomain) is another site entirely, otherwise I would get rid of the app subdomain.
So, I want app to be the default subdomain for all blueprints except api_1_0 which should be api.
Since you want your Flask application to handle multiple subdomains, you should set app.config['SERVER_NAME'] to the root domain. Then apply app as the default subdomain and overriding it in api blueprint registration.
The way to do this would be something like that I suppose:
app.config['SERVER_NAME'] = 'mydomain.com'
app.url_map.default_subdomain = "app"
app.register_blueprint(account, url_prefix='/account')
app.register_blueprint(users, url_prefix='/users')
app.register_blueprint(boxes, url_prefix='/boxes')
app.register_blueprint(api_1_0, subdomain='api')

Setting global attributes in Flask framework

I am working on a small web project using Flask/Python. This is a simple client side application without database.
I want to set the REST service address as a global attribute, but haven't figured out how to do that.
I know that attributes can be seted in flask.config like this:
app = Flask(__name__)
app.config['attribute_name'] = the_service_address
but the Blueprint module cannot access the 'app' object.
Thanks a lot for your time.
Within a request context (i.e. in a view/handler) you can access the config on the current_app
from flask import current_app
current_app.config['attribute_name']
You can do this like adding an attribute to any python object:
def create_web_app():
app = Flask('foods')
setattr(app, 'cheese', CheeseService())

Categories

Resources