emmett-framework / emmett

The web framework for inventors
BSD 3-Clause "New" or "Revised" License
1.06k stars 71 forks source link

Use Emmett ORM separately from the App (in a Notebook) #309

Open delarroqua opened 3 years ago

delarroqua commented 3 years ago

Hey @gi0baro ,

I wanted to know use Emmett ORM separately from the App.

Ideally, I would use a Notebook where I would declare the DB and the Models, attach the Models to the DB, and test different queries.

Is it possible ?

Thank you! Pierre

gi0baro commented 3 years ago

Hi @PierreDelarroqua, actually the plan is to have ORM separated into a dedicated package for 3.x release, which has no ETA (yet).

In the meantime, you have two ways to do what you ask: a) instantiate an application, even if you won't use it, eg

from emmett import App
from emmett.orm import Database

db = Database(App(__name__))

b) hack your database instance using a fake application object, eg

from collections import defaultdict
from emmett import sdict
from emmett.orm import Database

app = sdict(
    config=sdict(db=sdict()), 
    log=defaultdict(lambda: lambda *a, **b: None), 
    send_signal=lambda *a, **b: None
)
db = Database(app)

In both cases, you just need to explicitly open connections when running queries, eg:

with db.connection():
    Model.where(...)
delarroqua commented 3 years ago

@gi0baro thank you very much, that's great !

We'll use this right away !

delarroqua commented 3 years ago

@gi0baro sorry to bother you again.

In both cases, it doesn't work for us:

Error Case A:

---------------------------------------------------------------------------
LookupError                               Traceback (most recent call last)
<ipython-input-18-4054fec1aef8> in <module>
----> 1 app = App(__name__)

~/anaconda3/envs/vech/lib/python3.9/site-packages/emmett/app.py in __init__(self, import_name, root_path, url_prefix, template_folder, config_folder)
    214         #: finalise
    215         self._modules: Dict[str, AppModule] = {}
--> 216         current.app = self
    217 
    218     def _configure_asgi_handlers(self):

~/anaconda3/envs/vech/lib/python3.9/site-packages/emmett/ctx.py in __setattr__(self, name, value)
    105 
    106     def __setattr__(self, name: str, value: Any):
--> 107         setattr(self._ctx.get(), name, value)
    108 
    109     def __delattr__(self, name: str):

LookupError: <ContextVar name='ctx' at 0x7fb4ddc34c20>

Error Case B:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-29d61bc2989b> in <module>
----> 1 db = Database(app)

~/anaconda3/envs/vech/lib/python3.9/site-packages/pydal/base.py in __call__(cls, *args, **kwargs)
    168             setattr(cls, tup[0], tup[1])
    169 
--> 170         obj = super(MetaDAL, cls).__call__(*args, **kwargs)
    171         return obj
    172 

~/anaconda3/envs/vech/lib/python3.9/site-packages/emmett/orm/base.py in __init__(self, app, config, pool_size, keep_alive_timeout, connect_timeout, folder, **kwargs)
    123         #: set directory
    124         folder = folder or 'databases'
--> 125         folder = os.path.join(app.root_path, folder)
    126         if self._auto_migrate:
    127             with self._cls_global_lock_:

~/anaconda3/envs/vech/lib/python3.9/posixpath.py in join(a, *p)
     74     will be discarded.  An empty last part will result in a path that
     75     ends with a separator."""
---> 76     a = os.fspath(a)
     77     sep = _get_sep(a)
     78     path = a

TypeError: expected str, bytes or os.PathLike object, not NoneType

Any idea how to make it work ?

Thank you, Pierre

gi0baro commented 3 years ago

@PierreDelarroqua woah, the first traceback is quite a mistery to me. I'm definitely not sure how is it possible contextvars are not working :/

Regarding the 2nd one, probably adding an attribute for the root_path into your fake app object would make the job, eg:

app = sdict(
    config=sdict(db=sdict()), 
    log=defaultdict(lambda: lambda *a, **b: None), 
    send_signal=lambda *a, **b: None,
    root_path=os.getcwd()
)