Search code examples
pythonpymongofastapipydantic

How to validate email uniqueness with pydantic and fastapi


i'm trying to validate input email from request body and i have build custom field validator at model.

here is code of RegisterModel:

from pydantic import BaseModel, EmailStr, Field, field_validator
from repository.repository_user import UserRepository
from pprint import pprint 

class RegisterModel(BaseModel):
   name:str
   email:EmailStr 
   password:str = Field(..., min_length=7)

   @field_validator('email')
   @classmethod
   def email_must_unique(cls, v):
      repo = UserRepository()
      result = repo.find({'email': v})
      pprint(result)
      return result
    

the email_must_unique method will create new instace of UserRepository class and it will call the find method from UserRepository to find specific user based on email. actualy the email_must_unique method not finished yet to validate the email, it just get specific users, but i already facing an error.

here is code of UserRepository:

from pymongo.database import Database
from fastapi import Depends
from config.db import db_conn

class UserRepository:
    def __init__(self, db: Database = Depends(db_conn)):
        self.repository = db.users   # users is mongo collection

    def find(self, filter: dict):
        result = self.repository.find_one(filter)
        return result

with this code i'm facing error like this:

    self.repository = db.users
                      ^^^^^^^^
AttributeError: 'Depends' object has no attribute 'users'

i have no idea what What caused it. can you give me the solution to solve this error or maybe alternative way to validate email uniqueness ?


Solution

  • As mentioned in the comments by @Chiheb Nexus, Do not call the Depends function directly. Instead, use the Annotated dependency which is more graceful.

    For example, the config/db.py file should look like this:

    # config/db.py
    from typing import Annotated
    
    from fastapi import Depends
    from pymongo import MongoClient
    from pymongo.database import Database
    
    
    def get_db() -> Database:
        client = MongoClient("mongodb://localhost:27017/")
        return client.some_database
    
    
    DbConn = Annotated[Database, Depends(get_db)]  # <-- this is the annotated depend
    

    You can then use it like this:

    from config.db import DbConn
    
    
    class UserRepository:
        def __init__(self, db: DbConn):  # <- FastAPI will call `DbConn`
            self.repository = db.users
    
        def find(self, filter: dict):
            result = self.repository.find_one(filter)
            return result
    

    In addition, db in the __init__ parameter is annotated via DbConn so IDE knows its type.

    Validating the uniqueness of data is a task best left to the database rather than Pydantic. In other words, you should insert data and handle unique errors, DuplicateKeyError in pymongo.