ashleysommer / sanic-plugin-toolkit

Easily create Plugins for Sanic!
MIT License
51 stars 9 forks source link

Add context example? #5

Closed garyo closed 6 years ago

garyo commented 6 years ago

I'm trying to use SPF with the built-in Contextualize plugin to add context to my app; specifically I'd like to tie the Sqlalchemy database sessionmaker to the app so anywhere I have the app (I'd like to do this for requests too) I can create a db session. I looked at the Contextualize example but it wasn't clear to me how to add things to the app context (or maybe Contextualize doesn't do that?)

ashleysommer commented 6 years ago

Hi @garyo

The Contextualize plugin is a brand new feature in SPF, only added very recently. I haven't fully thought out the different ways users will use it, and I'm aware the single simple example is not very helpful.

I can do some more fully worked examples and so up some documentation for usuage, if it is something that users will want.

ashleysommer commented 6 years ago

@garyo

Ive been thinking about your use case.

Is it critical that you need a context attached to the App object? That's not really how SPF and the Contextualize plugin works.

The way this works is the context you get is attached to the Contextualize plugin, and the plugin is registered against the SPF, and the SPF is installed on the App object.

The context gets automatically injected into the middleware handler or route handler where you need it, as seen in the example file. No need to mess around with the App object.

It can do what you want it to do, but it's not quite the same as simply having the context directly on the App object.

garyo commented 6 years ago

Is it critical? I don't know, really -- I'm writing my first Sanic app with a sqlalachemy db, and just trying to figure out the best way to handle the database and its objects I may need at various points in my app (those seem to be: the Engine, the Model base class, and the sessionmaker). Right now I have those as globals in one of my modules, which works, but I was thinking since they're related to the app, and only initialized during app startup, it would be clean if they were attributes of the app somehow. I've seen that Flask has an app_context where things like this can get stored, and was hoping Contextualize would give me that. I guess I can just add my own context attribute directly to the app, e.g.

app._app_context = {'db': engine, 'dbsession': sessionmaker(bind=db)}

but that seems dangerous (what if something else wants to use _app_context?) and not that clean, e.g. what if someday app uses slots to prevent random attributes from being attached, or whatever. But maybe that's the best way?

ashleysommer commented 6 years ago

Yes, I understand that is how you want to do it. But as you said, it is not safe and not clean. That is why we don't do it that way in Sanic.

While Sanic is 'Flask-like' in a lot of ways, this is one of the cases where it is quite different. And mimicking the Flask-way and trying to use Sanic as if it was Flask is not a good idea.

The SPF 'shared context' is the closest thing we have a flask-like app_context. That is, it is a context object (a special dict) provided by SPF that all plugins and the app itself can access.

The Contextualize plugin can allow you to use the shared context in your app if you aren't writing your own plugin.

The contextualize example.py file mentioned above does show how to do it, but it may be not very clear.

In the next few days I'll create a specific sqalchemy example that shows exactly how to do what you want, using the Contextualize plugin.

ashleysommer commented 6 years ago

@garyo I realized that I'd forgotten to add the @listener capabilities to the Contextualize plugin.

With version 0.6.3.dev I've now added the @listener functionality to the Contextualize plugin, and I've added an example of how to use it to do what you described above. https://github.com/ashleysommer/sanicpluginsframework/blob/master/examples/contextualize_sqalchemy_example.py

Let me know if that is what you were looking for.

~EDIT:~ ~I can't release 0.6.3.dev to Pypi because TOX auto-pypi-upload won't right now. Will try again later. In the meantime you can install directly from git.~

EDIT: v 0.6.3.dev is now out on PyPI

ashleysommer commented 6 years ago

closed via https://github.com/ashleysommer/sanicpluginsframework/commit/a8b95cc9b57c6e430bd1c82053785a89a687936a

garyo commented 6 years ago

Hi Ashley, yes this looks just like what I need. I think it's best not to create a long-running dbsession for the worker, but just to add the engine and sessionmaker to the app, and then create a dbsession for each request (and similar for websocket requests). But this example now has all the info I need to get that going.

ashleysommer commented 6 years ago

@garyo Yes, I just took that example code from a sqlalchemy tutorial site and adapted it to make my example. Of course you can take the general idea to do whatever db setup routine you like. I'm glad to have been able to help you out with your issue.