Created
April 26, 2023 01:27
-
-
Save rodrigoddc/a8d6b1e617427513f7c1f85e746872e8 to your computer and use it in GitHub Desktop.
SQLAlchemy ORM models + Pydantic with custom composite Field
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from uuid import UUID | |
| from pydantic.class_validators import validator | |
| from pydantic.main import BaseModel | |
| from pydantic.types import UUID4 | |
| from sqlalchemy import Column, String, Uuid | |
| from sqlalchemy.orm import declarative_base | |
| Base = declarative_base() | |
| class ContactModel(Base): | |
| __tablename__ = "contacts" | |
| id = Column(Uuid, primary_key=True) | |
| email = Column(String, nullable=False) | |
| class Email(BaseModel): | |
| value: str | |
| class Config: | |
| orm_mode = True | |
| class Id(BaseModel): | |
| value: UUID4 | |
| class Config: | |
| orm_mode = True | |
| def default_parser(value: str | dict) -> dict: | |
| if isinstance(value, str) and not value: | |
| raise ValueError("value cannot be empty") | |
| if isinstance(value, str) or isinstance(value, UUID): | |
| return {'value': value} | |
| return value | |
| class Contact(BaseModel): | |
| id: Id | |
| email: Email | |
| class Config: | |
| orm_mode = True | |
| json_encoders = { | |
| Id: lambda v: str(v.value), | |
| Email: lambda v: v.value, | |
| } | |
| _parse_id = validator("id", pre=True, allow_reuse=True)(default_parser) | |
| _parse_email = validator("email", pre=True, allow_reuse=True)(default_parser) | |
| def dict(self, *args, **kwargs): | |
| base_dict = super().dict(*args, **kwargs) | |
| for field, value in base_dict.items(): | |
| if isinstance(value, dict) and 'value' in value: | |
| base_dict[field] = value['value'] | |
| return base_dict | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import pytest | |
| from uuid import UUID | |
| from pydantic.error_wrappers import ValidationError | |
| from main import Email, Contact, ContactModel, Id | |
| def test_email_schema(): | |
| email = Email(value='[email protected]') | |
| assert email.value == '[email protected]' | |
| def test_contact_schema_id_as_dict(): | |
| contact = Contact( | |
| **{ | |
| 'id': {'value': 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'}, | |
| 'email': {'value': '[email protected]'} | |
| } | |
| ) | |
| assert contact.id == Id(value=UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11')) | |
| assert contact.email.value == '[email protected]' | |
| def test_contact_schema_id_as_str(): | |
| contact = Contact( | |
| **{ | |
| 'id': 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', | |
| 'email': {'value': '[email protected]'} | |
| } | |
| ) | |
| assert contact.id == Id(value=UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11')) | |
| assert contact.email.value == '[email protected]' | |
| def test_contact_schema_email_as_dict(): | |
| contact = Contact( | |
| **{ | |
| 'id': 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', | |
| 'email': {'value': '[email protected]'} | |
| } | |
| ) | |
| assert contact.id == Id(value=UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11')) | |
| assert contact.email.value == '[email protected]' | |
| def test_contact_schema_email_as_str(): | |
| contact = Contact( | |
| **{ | |
| 'id': Id(value=UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11')), | |
| 'email': '[email protected]' | |
| } | |
| ) | |
| assert contact.id == Id(value=UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11')) | |
| assert contact.email.value == '[email protected]' | |
| def test_contact_schema_dict_representation(): | |
| contact = Contact( | |
| id=Id(value=UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11')), | |
| email='[email protected]' | |
| ) | |
| assert contact.dict() == { | |
| 'id': UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'), | |
| 'email': '[email protected]' | |
| } | |
| def test_contact_schema_json_representation(): | |
| contact = Contact( | |
| id=Id(value=UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11')), | |
| email='[email protected]' | |
| ) | |
| assert contact.json(models_as_dict=False) == '{"id": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", "email": "[email protected]"}' | |
| assert contact.json() == '{"id": {"value": "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"}, "email": {"value": "[email protected]"}}' | |
| def test_contact_schema_raises_when_id_empty_string(): | |
| with pytest.raises(ValidationError) as e: | |
| contact = Contact( | |
| id='', | |
| email='[email protected]' | |
| ) | |
| assert 'value cannot be empty' in str(e.value) | |
| def test_contact_schema_raises_when_email_empty_string(): | |
| with pytest.raises(ValidationError) as e: | |
| contact = Contact( | |
| id=Id(value=UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11')), | |
| email='' | |
| ) | |
| assert 'value cannot be empty' in str(e.value) | |
| def test_contact_schema_from_model(): | |
| model = ContactModel( | |
| id=UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'), | |
| email='[email protected]' | |
| ) | |
| contact = Contact.from_orm(model) | |
| assert contact.id == Id(value=UUID('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11')) | |
| assert contact.email.value == '[email protected]' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment