Package: pydbantic
Persistent pydantic models - A single model for shaping, creating, accessing, storing data within a DatabaseKey FeaturesFlexible Data TypesOne Model for type validation & database accessDynamic / Explicit Model RelationshipsIntegrated Redis Caching SupportBuilt in support for Alembic Migrations or Automatic Migration on Schema ChangesDocumentationhttps://pydbantic.readthedocs.io/en/latest/Setup$pipinstallpydbantic
$pipinstallpydbantic[sqlite]$pipinstallpydbantic[mysql]$pipinstallpydbantic[postgres]Basic Usage - ModelfromtypingimportList,Optional,UnionfrompydbanticimportDataBaseModel,PrimaryKey,UniqueclassDepartment(DataBaseModel):department_id:str=PrimaryKey()name:str=Unique()company:stris_sensitive:bool=Falsepositions:List[Optional['Positions']]=[]# One to ManyclassPositions(DataBaseModel):position_id:str=PrimaryKey()name:strdepartment:Department=None# One to One mappingemployees:List[Optional['Employee']]=[]# One to ManyclassEmployeeInfo(DataBaseModel):ssn:str=PrimaryKey()first_name:strlast_name:straddress:straddress2:Optional[str]city:Optional[str]zip:Optional[int]new:Optional[str]employee:Optional[Union['Employee',dict]]=None# One to OneclassEmployee(DataBaseModel):employee_id:str=PrimaryKey()employee_info:Optional[EmployeeInfo]=None# One to Oneposition:List[Optional[Positions]]=[]# One to Manysalary:floatis_employed:booldate_employed:Optional[str]Basic Usage - Connecting a Database to ModelsimportasynciofrompydbanticimportDatabasefrommodelsimportEmployee,EmployeeInfo,Positions,Departmentasyncdefmain():db=awaitDatabase.create('sqlite:///test.db',tables=[Employee,EmployeeInfo,Positions,Department])if__name__=='__main__':asyncio.run(main())Model UsageImport and use the models where you need them. As long as DB as already been created,
the Models can access the & Use the connected DBfrommodelsimport(Employee,EmployeeInfo,Position,Department)Model - Creation# create departmenthr_department=awaitDepartment.create(id='d1234',name='hr'company='abc-company',is_sensitive=True,)Via instance using insert or savehr_department=Department.create(id='d1234',name='hr'company='abc-company',is_sensitive=True,)awaithr_department.insert()awaithr_department.save()Insert with related models# create a Position in Hr Departmenthr_manager=Position.create(id='p1234',name='manager',department=hr_department)# create instance on an hr employeehr_emp_info=EmployeeInfo.create(ssn='123-456-789',first_name='john',last_name='doe',address='123 lane',city='snake city',zip=12345)# create an hr employeehr_employee=awaitEmployee.create(id='e1234',employee_info=hr_emp_info,position=hr_manager,is_employed=True,date_employed='1970-01-01')Filtering# get all hr managers currently employedmanagers=awaitEmployee.filter(Employee.position==hr_manager,# conditionalis_employed=True# key-word argument)first_100_employees=awaitEmployee.all(limit=100)See also filteringoperatorsDeleting# one by oneformanagerinawaitEmployee.filter(position=hr_manager,is_employed=False):awaitmanager.delete()# all at onceawaitEmployee.delete_filter(Employee.is_employed==True)Updating# raise salary of all managersformanagerinawaitEmployee.filter(position=hr_manager,is_employed=False):manager.salary=manager.salary+1000.0awaitmanager.update()# or manager.save().save()results in a new row created inEmployeetable as well as the relatedEmployeeInfo,Position,Departmenttables if not yet created.  sWhat is pydbanticpydbanticwas built to solve some of the most common pain developers may face working with databases.migrationsmodel creation / managementdynamic relationshipscachingpydbanticbelieves that related data should be stored together, in the shape the developer plans to usepydbanticknows data is rarely flat or follows a set schemapydbanticunderstand migrations are not fun, and does them for youpydbanticspeaks manytypesPillarspydantic- Models, Type validationdatabases- Database Connection Abstractionssqlalchemy- Core Database Query and Database ModelModelspydbanticmost basic object is aDataBaseModel. This object may be comprised of almost anypickle-ablepython object, though you are encouraged to stay within the type-validation land by usingpydantic'sBaseModelsand validators.Primary KeysDataBaseModel's also have a primary key, which is the first item defined in a model or marked with= PrimaryKey()classNotesBm(DataBaseModel):id:str=PrimaryKey()text:Optional[str]# optionaldata:DataModel# requiredcoordinates:tuple# requireditems:list# requirednested:dict={'nested':True}# Optional - w/ DefaultModel Types & TypingDataBaseModelitems are capable of being multiple layers deep followingpydanticmodel validationPrimary Key - First Item, must be uniqueRequired - items without default values are assumed requiredOptional - marked explicitly withtyping.Optionalor with a default valueUnion - Accepts Either specified input type Union[str|int]List[item] - Lists of specified itemsInput data-types without a standard built-in db serialization, are serialized usingpickleand stored as bytes. More on this later.Migrationspydbanticcan handle migrations automatically in response to detected model changes:New Field,Removed Field,Modified Field,Renamed Field,Primary Key Changes.pydbanticis also able to integrate with Alembic to perform migrations, seeMigrations Using AlembicCacheAdding cache with Redis is easy withpydbantic, and is complete with built incache invalidation. I.E when a query becomes outdated(from insertion / update / deletion), it will be expelled from cache.db=awaitDatabase.create('sqlite:///test.db',tables=[Employee],cache_enabled=True,redis_url="redis://localhost")Models with arrays of Foreign ObjectsDataBaseModelmodels can support arrays of bothBaseModelsand otherDataBaseModel. Just like singleDataBaseModelreferences, data is stored in separate tables, and populated automatically when the childDataBaseModelis instantiated.fromuuidimportuuid4fromdatetimeimportdatetimefromtypingimportList,OptionalfrompydbanticimportDataBaseModel,PrimaryKeydeftime_now():returndatetime.now().isoformat()defget_uuid4():returnstr(uuid4())classCoordinate(DataBaseModel):id:str=PrimaryKey(default=get_uuid4)lat_long:tuplejourneys:List[Optional["Journey"]]=[]classJourney(DataBaseModel):trip_id:str=PrimaryKey(default=get_uuid4)waypoints:List[Optional[Coordinate]]=[]