К хорошему быстро привыкаешь, и при переходе от одного инструмента, если лишаешься, чего то привычного, то становится некомфортно. Именно с такой ситуацией я столкнулся при переходе от такого замечательного фреймворка как Django, на FastApi.
Как оказалось SqlAlcemy которую я решил использовать как ORM для своего проекта также не имеет встроенных средств, наподобие тех, что реализованы в Django.

Рванувшись в гугл с этим вопросом я наткнулся на решение которое многие советовали, а именно использовать библиотку sqlalchemy-utils и встроенный в нее тип ChoicesType.
Собственно я так и поступил, поставил библиотеку, установил в соответствующий тип нужные мне поля и столкнулся с тем, что Pydantic не может нормально валидировать ChoicesType данной библиотеки. После длительных мучений было найдено решение валидировать поле и самому возвращать нужное значение в валидаторе. Но все же я решил отказаться от использования данной библиотеки и все что нужно самому написать с нуля. Итак поехали.

ChoicesType строкового типа

В sqlalchemy определяем наши модели. У меня получился примерно следующий код.

import uuid, enum
from sqlalchemy.dialects.postgresql import UUID

from sqlalchemy import Boolean, Column, Integer, String, Date, ForeignKey, Enum
from sqlalchemy.orm import relationship
from app.models.mixins import Timestamp, generic_repr
from app.db.base_class import Base

class SpecializationDoctorEnum(enum.Enum):
    """Специализации врачей"""
    terapevt = 'Терапевт'
    ortodont = 'Ортодонт'
    ortoped ='Ортопед'
    parodontolog = 'Пародонтолог'
    detckii_stomatolog = 'Детский стоматолог'
    hirurg = 'Хирург'

@generic_repr
class User(Base, Timestamp):
    '''Модель пользователя'''
    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    first_name = Column(String, index=True)
    last_name = Column(String, index=True)
    midle_name = Column(String, index=True)
    birthday = Column(Date)
    image = Column(String, nullable=True)
    email = Column(String, unique=True, index=True, nullable=False)
    hashed_password = Column(String, nullable=False)
    specialization = Column(Enum(SpecializationDoctorEnum), default=SpecializationDoctorEnum.terapevt, nullable=False)
    is_active = Column(Boolean(), default=True)
    is_superuser = Column(Boolean(), default=False)
    clinic_id = Column(Integer, ForeignKey('clinic.id'))
    clinic = relationship("Clinic", back_populates="users")

Ну и соответствующая Pydantic модель.

from typing import Optional
from enum import Enum
from uuid import UUID

from pydantic import BaseModel, EmailStr, validator
from datetime import date

from app.schemas.clinic import Clinic
from app.models.user import SpecializationDoctorEnum


class Specialization(str, Enum):
    terapevt = 'Терапевт'
    ortodont = 'Ортодонт'
    ortoped ='Ортопед'
    parodontolog = 'Пародонтолог'
    detckii_stomatolog = 'Детский стоматолог'
    hirurg = 'Хирург'

# Shared properties
class UserBase(BaseModel):
    email: Optional[EmailStr]
    is_active: Optional[bool] = True
    is_superuser: bool = False
    first_name: Optional[str]
    last_name: Optional[str]   
    midle_name: Optional[str]    
    birthday: Optional[date]
    image: Optional[str]
    clinic: Optional[Clinic]
    specialization: Optional[SpecializationDoctorEnum]

    class Config:
            orm_mode = True

Ну собственно теперь мы сможем употреблять в коде наши значения примерно так

new_user.specialization = SpecializationDoctorEnum.terapevt

ChoicesType со значениями типа Integer

В данном случае у нас будет все то же самое только в определении моделей sqlAlchemy мы будем использовать enum.IntEnum.

class NumberJobsEnum(enum.IntEnum):
    none = 0
    one = 1
    two = 2
    three = 3
    four = 4
    five = 5
class Person(db.Model):
    __tablename__ = 'persons'
    number_of_jobs = db.Column(
        IntEnum(NumberJobsEnum), 
        default=NumberJobsEnum.none,
        nullable=False
    )

Заключение

Не всегда целесообразно для реализации простых вещей тянуть целые библиотеки, особенно если за ними не стоит большое активное сообщество. В моем случае при помощи встроенного Enum типа библиотеки sqlalchemy удалось реализовать весь необходимый функционал.


Опубликовано

в

от

Метки:

Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Яндекс.Метрика