Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Models entity #64

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion dvhb_hybrid/amodels/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
from .decorators import method_connect_once, method_redis_once
from .model import Model
from .mptt_mixin import MPTTMixin
from .entity import ContextModels
from .. import utils


__all__ = [
'AppModels',
'ContextModels',
'Model',
'derive_from_django',
'method_connect_once',
Expand All @@ -30,7 +32,7 @@ def __getitem__(self, item):
def __getattr__(self, item):
if item in Model.models:
model_cls = Model.models[item]
sub_class = model_cls.factory(self.app)
sub_class = model_cls.factory(app=self.app)
setattr(self, item, sub_class)
if hasattr(model_cls, 'relationships'):
for k, v in model_cls.relationships.items():
Expand Down
11 changes: 6 additions & 5 deletions dvhb_hybrid/amodels/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,20 +116,21 @@ def convert_m2m(field):
else:
raise ConversionError('Unknown many to many field: %r' % field)

def m2m_factory(app):
def m2m_factory(app=None, context=None):
models = context.models if context is not None else app.m
model_name = convert_class_name(dj_model.__name__)
if hasattr(app.m, model_name):
if hasattr(models, model_name):
# Get existing relationship model
model = getattr(app.m, model_name)
model = getattr(models, model_name)
else:
# Create new relationship model
model = type(dj_model.__name__, (Model,), {})
model.table = model.get_table_from_django(dj_model)
model = model.factory(app)
model = model.factory(app=app, context=context)

# Note that async model's name should equal to corresponding django model's name
target_model_name = convert_class_name(field.related_model.__name__)
target_model = getattr(app.m, target_model_name)
target_model = getattr(models, target_model_name)

return ManyToManyRelationship(app, model, target_model, source_field, target_field)

Expand Down
9 changes: 5 additions & 4 deletions dvhb_hybrid/amodels/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from weakref import WeakKeyDictionary

from .debug import ConnectionLogger
from ..utils import get_app_from_parameters
from ..utils import get_app_from_parameters, get_context_from_parameters


class Guard:
Expand All @@ -30,9 +30,10 @@ def with_arg(func):
@functools.wraps(func)
async def wrapper(*args, **kwargs):
if kwargs.get('connection') is None:
app = get_app_from_parameters(*args, **kwargs)
with Guard('pg', app.loop):
async with app['db'].acquire() as connection:
context = get_context_from_parameters(*args, **kwargs)
with Guard('pg', context.loop):
# db is predefined key for postgres in context
async with context.db.acquire() as connection:
kwargs['connection'] = ConnectionLogger(connection)
return await func(*args, **kwargs)
else:
Expand Down
54 changes: 54 additions & 0 deletions dvhb_hybrid/amodels/entity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import importlib
import logging
import pkgutil
import sys

from aioworkers.core.base import AbstractEntity

from . import Model

logger = logging.getLogger(__name__)


class ContextModels(AbstractEntity):
def __init__(self, config=None, *, context=None, loop=None):
super().__init__(config, context=context, loop=loop)
self.db = None
self.redis = None
self._module = self.config.get('module', 'amodels')
self._search_models()

async def init(self):
# Models require postgres and redis
self.db = self.context[self.config.get('db', 'db')]
self.redis = self.context[self.config.get('redis', 'redis')]

def _search_models(self):
"""
Search for models in specified package
"""
package = sys.modules[self.config.package]
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__):
if ispkg:
try:
m = '{}.{}.{}'.format(package.__name__, modname, self._module)
importlib.import_module(m)
logger.info(f"Load module {m}")
except ImportError:
pass

def __getitem__(self, item):
if hasattr(self, item):
return getattr(self, item)
return KeyError(item)

def __getattr__(self, item):
if item in Model.models:
model_cls = Model.models[item]
sub_class = model_cls.factory(context=self.context)
setattr(self, item, sub_class)
if hasattr(model_cls, 'relationships'):
for k, v in model_cls.relationships.items():
setattr(sub_class, k, v(app=self.context.app, context=self.context))
return sub_class
raise AttributeError('%r has no attribute %r' % (self, item))
5 changes: 3 additions & 2 deletions dvhb_hybrid/amodels/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def __new__(mcls, name, bases, namespace):
class Model(dict, metaclass=MetaModel):
models = {}
app = None
context = None
primary_key = 'id'
validators = () # Validators for data
update_validators = () # Validators to validate object and data before update
Expand All @@ -47,8 +48,8 @@ class Model(dict, metaclass=MetaModel):
fields_localized = None

@classmethod
def factory(cls, app):
return type(cls.__name__, (cls,), {'app': app})
def factory(cls, app=None, context=None):
return type(cls.__name__, (cls,), {'app': app, 'context': context})

@classmethod
def get_cache_key(cls, *args):
Expand Down
2 changes: 2 additions & 0 deletions dvhb_hybrid/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,6 @@ async def init(connection):

async with asyncpgsa.create_pool(*dbargs, init=init, **dbkwargs) as pool:
app[app_key] = pool
app.context['databases.' + cfg_key] = pool
app.context[app_key] = pool # TODO delete?
yield
14 changes: 14 additions & 0 deletions dvhb_hybrid/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,17 @@ def get_app_from_parameters(*args, **kwargs):
return i.request.app
elif hasattr(i, '_context'):
return i._context.app


def get_context_from_parameters(*args, **kwargs):
if kwargs.get('request') is not None:
return kwargs['request'].app.context
for i in args:
if getattr(i, 'context', None) is not None:
return i.context
elif hasattr(i, 'request'):
return i.request.app.context
elif getattr(i, 'app', None) is not None:
return i.app.context
elif getattr(i, '_context', None) is not None:
return i._context