예제 #1
0
파일: test_main.py 프로젝트: vanutp/fox_orm
    async def test_relationship_generation(self):
        from sqlalchemy import MetaData, Integer
        from fox_orm import OrmModel
        from fox_orm.fields import pk
        from fox_orm.relations import ManyToMany

        metadata = MetaData()

        class RelA(OrmModel):
            __metadata__ = metadata
            pkey: Optional[int] = pk
            b_objs: ManyToMany['B'] = ManyToMany(to='placeholder', via='mid')

        class RelB(OrmModel):
            __metadata__ = metadata
            pkey: Optional[int] = pk
            a_objs: ManyToMany['A'] = ManyToMany(to=RelA, via='mid')

        RelA.__relations__['b_objs']._to = RelB

        FoxOrm.init_relations(metadata)

        self.assertIn('mid', metadata.tables)
        self.assertNotIn('b_objs', metadata.tables['rel_a'].columns)
        self.assertNotIn('a_objs', metadata.tables['rel_b'].columns)
        self.assertEqual(schema_to_set(metadata.tables['mid']), {
            ('rel_a_id', Integer),
            ('rel_b_id', Integer),
        })
예제 #2
0
파일: relations.py 프로젝트: vanutp/fox_orm
 def _init(self, metadata: MetaData, _from: 'Type[OrmModel]'):
     self._from = _from
     if isinstance(self._to, str):
         self._to = full_import(self._to)
     self._via, self._this_id, self._other_id = FoxOrm.get_assoc_table(
         metadata, self._from, self._to, self._via_name
     )
     self._initialized = True
예제 #3
0
파일: test_perf.py 프로젝트: vanutp/fox_orm
import asyncio
import os
from time import time

from sqlalchemy import create_engine

from fox_orm import FoxOrm
from tests.models import A

DB_FILE = 'test.db'
DB_URI = 'sqlite:///test.db'

if os.path.exists(DB_FILE):
    os.remove(DB_FILE)
FoxOrm.init(DB_URI)
FoxOrm.metadata.create_all(create_engine(DB_URI))

ITERATIONS = 300


async def main():
    print('Simple insert')
    time_start = time()
    for i in range(ITERATIONS):
        await FoxOrm.db.execute(A.__table__.insert(), {
            'text': 'test',
            'n': i,
        })
    print('- Databases', (time() - time_start) / ITERATIONS)

    time_start = time()
예제 #4
0
    a_objs: ManyToMany[A] = ManyToMany(to='tests.models.A', via='mid')
    c_objs: OneToMany['C'] = OneToMany(to='tests.models.C', key='b_id')


class C(OrmModel):
    pkey: Optional[int] = pk
    b_id: Optional[int]
    d_id: Optional[int]


class D(OrmModel):
    pkey: Optional[int] = pk
    c_objs: OneToMany['C'] = OneToMany(to='tests.models.C', key='d_id')


class E(OrmModel):
    pkey: Optional[int] = pk
    dt: datetime.datetime


class ExtraFields(OrmModel):
    class Config:
        extra = Extra.allow

    pkey: Optional[int] = pk
    _test: str


FoxOrm.init_relations()
예제 #5
0
파일: model.py 프로젝트: vanutp/fox_orm
class OrmModelMeta(ModelMetaclass):
    if TYPE_CHECKING:
        __columns__: Dict[str, Column]
        __table__: Table
        __relations__: dict
        __tablename__: str
        __metadata__: MetaData
        __abstract__: bool
        __pkey_name__: str

    @property
    def pkey_column(cls):
        return getattr(cls.__table__.c, cls.__pkey_name__)

    @classmethod
    def _check_type(mcs, namespace: dict, key: str, expected_type: type):
        if key in namespace and not isinstance(namespace[key], expected_type):
            raise OrmException(
                f'{key} must be of type {expected_type.__qualname__}')

    @classmethod
    def _ensure_proper_init(mcs, namespace):
        if '__sqla_table__' in namespace:
            raise OrmException(
                'You are using pre 0.3 model syntax. Check the docs for new instructions'
            )
        if '__table__' in namespace:
            raise OrmException('__table__ should not be set')
        mcs._check_type(namespace, '__tablename__', str)
        mcs._check_type(namespace, '__metadata__', MetaData)
        mcs._check_type(namespace, '__abstract__', bool)

    def get_namespace(cls):
        namespace = {}
        namespace['__annotations__'] = annotations = {}
        for base in cls.mro():
            if not (issubclass(base, OrmModel) and base is not OrmModel):
                break
            for column_name, namespace_value in base.__fields__.items():
                namespace[column_name] = namespace_value
            for column_name, annotation in base.__annotations__.items():
                annotations[column_name] = annotation
        return namespace

    def __getattribute__(cls, item):
        caller = traceback.extract_stack()[-2]
        if caller.name == 'validate_field_name' and caller.filename.endswith(
                f'{os.sep}pydantic{os.sep}utils.py'):
            return super().__getattribute__(item)

        if item.startswith('_'):
            return super().__getattribute__(item)

        if item in cls.__columns__:
            return getattr(cls.__table__.columns, item)
        if item in cls.__relations__:
            return cls.__relations__[item]

        return super().__getattribute__(item)

    def __new__(mcs, name, bases, namespace, **kwargs):
        if bases[0] == BaseModel:
            return super().__new__(mcs, name, bases, namespace, **kwargs)

        inherited_columns = {}
        for base in bases[::-1]:
            if issubclass(base, OrmModel) and base is not OrmModel:
                inherited_columns.update(
                    {x.name: x.copy()
                     for x in base.__columns__.values()})

        mcs._ensure_proper_init(namespace)

        table_name = namespace.get('__tablename__',
                                   None) or camel_to_snake(name)
        metadata = namespace.get('__metadata__', None) or FoxOrm.metadata
        abstract = namespace.get('__abstract__', None) or False

        new_namespace = {}
        relation_namespace = {}
        for k, v in namespace.items():
            if k == '__tablename__':
                continue
            if isinstance(v, _GenericIterableRelation):
                relation_namespace[k] = v
            else:
                new_namespace[k] = v

        new_namespace['__annotations__'] = annotations = {}
        for k, v in namespace.get('__annotations__', {}).items():
            if k in relation_namespace:
                continue
            annotations[k] = v

        columns = {}
        for column_name, namespace_value in new_namespace.items():
            if not is_valid_column_name(
                    column_name) or not is_valid_column_value(namespace_value):
                continue
            if column_name not in annotations:
                raise OrmException(f'Unannotated field {column_name}')
            column, value = construct_column(column_name,
                                             annotations[column_name],
                                             namespace_value)
            columns[column.name] = column
            new_namespace[column_name] = value
        for column_name, annotation in annotations.items():
            if not is_valid_column_name(column_name):
                continue
            if column_name not in columns:
                column, _ = construct_column(column_name, annotation, tuple())
                columns[column.name] = column
        all_columns = inherited_columns.copy()
        all_columns.update(columns)

        # Hack for generating FastAPI models
        if (len(bases) == 1 and issubclass((base := bases[0]), OrmModel)
                and base is not OrmModel and base.__name__ == name):
            stack = traceback.extract_stack()
            if len(stack) >= 3:
                caller = stack[-3]
                if caller.name == 'create_cloned_field' and caller.filename.endswith(
                        f'{os.sep}fastapi{os.sep}utils.py'):
                    return ModelMetaclass(name, (BaseModel, ),
                                          base.get_namespace())

        new_namespace['__abstract__'] = abstract
        new_namespace['__columns__'] = all_columns
        if abstract:
            new_namespace['__pkey_name__'] = None
            new_namespace['__table__'] = None
            new_namespace['c'] = None
        else:
            if sum([x.primary_key for x in all_columns.values()]) != 1:
                raise OrmException('Model should have exactly one primary key')
            new_namespace['__pkey_name__'] = [
                x.name for x in all_columns.values() if x.primary_key
            ][0]
            new_namespace['__table__'] = table = Table(table_name, metadata,
                                                       *all_columns.values())
            new_namespace['c'] = table.c
        new_namespace['__relations__'] = relation_namespace

        cls = super().__new__(mcs, name, bases, new_namespace, **kwargs)
        if not abstract:
            for rel in relation_namespace.values():
                FoxOrm._lazyinit_relation(metadata, rel, cls)
        return cls
예제 #6
0
파일: test_main.py 프로젝트: vanutp/fox_orm
 def setUpClass(cls):
     if os.path.exists(DB_FILE):
         os.remove(DB_FILE)
     FoxOrm.init(DB_URI)
     cls.engine = create_engine(DB_URI)
     FoxOrm.metadata.create_all(cls.engine)
예제 #7
0
async def connect():
    if os.path.exists(DB_FILE):
        os.remove(DB_FILE)
    FoxOrm.metadata.create_all(create_engine(DB_URI))
    FoxOrm.init(DB_URI)
    await FoxOrm.connect()