collerek / ormar

python async orm with fastapi in mind and pydantic validation
https://collerek.github.io/ormar/
MIT License
1.68k stars 89 forks source link

objects.create doesn't use __init__ or pydantic validator #245

Closed jinserk closed 3 years ago

jinserk commented 3 years ago

Describe the bug As discussed in https://github.com/collerek/ormar/discussions/230, an ormar.Model doesn't use __init__() or pydantic's validator when its object.create() is used

To Reproduce

from typing import ClassVar                                                                                                                                                                                                                                                     
import sqlite3                                                                                                                                                                                                                                                                  
import uuid                                                                                                                                                                                                                                                                     

import sqlalchemy                                                                                                                                                                                                                                                               
import databases                                                                                                                                                                                                                                                                
import ormar                                                                                                                                                                                                                                                                    
from pydantic import root_validator                                                                                                                                                                                                                                             

class Connection(sqlite3.Connection):                                                                                                                                                                                                                                           

    def __init__(self, *args, **kwargs):  # pragma: no cover                                                                                                                                                                                                                    
        super().__init__(*args, **kwargs)                                                                                                                                                                                                                                       
        self.execute("PRAGMA auto_vacuum = 1;")                                                                                                                                                                                                                                 
        self.execute("PRAGMA foreign_keys = 1;")                                                                                                                                                                                                                                
        self.execute("PRAGMA journal_mode = wal;")                                                                                                                                                                                                                              
        self.execute(f"PRAGMA cache_size = {2 ** 13};")                                                                                                                                                                                                                         
        self.execute(f"PRAGMA mmap_size = {2 ** 30};")

DB_URL = "sqlite:///local.db"

database = databases.Database(DB_URL, force_rollback=True, factory=Connection)
metadata = sqlalchemy.MetaData()

class BaseMeta(ormar.ModelMeta):
    database = database
    metadata = metadata

class Mol(ormar.Model):
    # fixed namespace to generate always unique uuid from the smiles
    _UUID_NAMESPACE: ClassVar[uuid.UUID] = uuid.UUID('12345678-abcd-1234-abcd-123456789abc')

    class Meta(BaseMeta):
        tablename = "mols"

    id: str = ormar.UUID(primary_key=True, index=True, uuid_format='hex')
    smiles: str = ormar.String(nullable=False, unique=True, max_length=256)

    def __init__(self, **kwargs):
        # this is required to generate id from smiles in init, if id is not given
        if 'id' not in kwargs:
            kwargs['id'] = self._UUID_NAMESPACE
        super().__init__(**kwargs)

    @root_validator()
    def make_canonical_smiles_and_uuid(cls, values):
        values['id'], values['smiles'] = cls.uuid(values['smiles'])
        return values

    @classmethod
    def uuid(cls, smiles):
        id = uuid.uuid5(cls._UUID_NAMESPACE, smiles)
        return id, smiles

engine = sqlalchemy.create_engine(DB_URL)
metadata.create_all(engine)

async def mol_subtask():
    mol = await Mol.objects.create(smiles="Cc1ccccc1")
    c = await Mol.objects.count()

asyncio.run(mol_subtask())

Expected behavior Generate a Mol object without error since the missing id will be generated from __init__ or validator

Versions (please complete the following information):

collerek commented 3 years ago

Fixed in 0.10.12 (via #251)