Pydantic: Uniqueness checks with pydantic

Created on 25 Apr 2020  路  4Comments  路  Source: samuelcolvin/pydantic

I am writing a few apis with SQLAlchemy, Pydantic, and FastAPI and I'm wondering if the following approach would be discouraged and/ or considered an anti-pattern.

Output of python -c "import pydantic.utils; print(pydantic.utils.version_info())":

$ python -c "import pydantic.utils; print(pydantic.utils.version_info())"
             pydantic version: 1.3
            pydantic compiled: False
                 install path: /Users/anthcor/.pyenv/versions/3.7.5/lib/python3.7/site-packages/pydantic
               python version: 3.7.5 (default, Dec 23 2019, 19:10:54)  [Clang 11.0.0 (clang-1100.0.33.16)]
                     platform: Darwin-19.4.0-x86_64-i386-64bit
     optional deps. installed: ['email-validator']

Here's an example:

from pydantic import BaseModel, EmailStr, validator
from typing import Optional

class User(BaseModel):
    name: str
    email: EmailStr
    bio: Optional[str]

    @validator('email')
    def unique_email(cls: BaseModel, v: EmailStr) -> EmailStr:
        db = next(get_db())
        users = db.query(UserModel).all()
        db.commit()
        if str(v) in set(map(lambda u: u.email, users)):
            raise ValueError('Email already in use.')
        return v

What's great about this is getting the appropriate 422 and error message details on an invalid submission where the email is already in use, but having a database call in what's supposed to be a declaration of types is making me think again.

Thanks in advance for any advice! Cheers.

question

Most helpful comment

Yes.

But it's not that hard to raise real pydantic errors from outside a validator, I'll give you an example tommorow when I'm at my desk, and one day we might have a utility for it.

All 4 comments

After some digging in issues I found this which notes that this is a discouraged practice.

Yes.

But it's not that hard to raise real pydantic errors from outside a validator, I'll give you an example tommorow when I'm at my desk, and one day we might have a utility for it.

Sounds awesome, thanks!

Howdy @samuelcolvin, from reading through some docs, I was thinking of writing decorators for these uniqueness checks similar to https://pydantic-docs.helpmanual.io/usage/validation_decorator/#raw-function.

However I'm thinking that raising pydantic validation errors in these sorts of situations may not be appropriate as I'm unsure of how to define/ assign a "unique" type to a parameter as that parameter would also need some context to evaluate the parameter. Feels like I'm trying to use pydantic for something that should just be raised as an HTTPException with FastAPI.

I did have an idea though, could you assign a "unique" type to a parameter via a context validator that would read in an iterable context parameter somehow? Is it possible to make a pydantic object context aware when being passed into a FastAPI route?

# pydantic ...


class NodeCreate(BaseModel):
    id: int
    name: str

    @context_validator('name')
    def unique_name(cls: BaseModel, v: str, context: Iterable = []) -> str:
        if v in context:
            raise ValueError('Node name is already taken!')
        return v


# fastapi ...


def create_node(db: Session = Depends(get_db),
                node_create: NodeCreate = Body(
                    ..., example={
                        'id': 0,
                        'name': 'alpha'
                    })):
    # set context
    context = set(map(lambda n: n.name, db.query(Node).all()))
    # value error is raised if name is in context?
    node_create.context = context
    # save result if node_create is valid w respect to context and move along
    result = node_helper.save_node(db, node_create)
    return result

Lmk if this sounds totally bonkers. Thanks again.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sbv-trueenergy picture sbv-trueenergy  路  3Comments

drpoggi picture drpoggi  路  3Comments

gangefors picture gangefors  路  3Comments

dconathan picture dconathan  路  3Comments

cdeil picture cdeil  路  3Comments