Repositories
Repository pattern is a way to abstract the data layer from the business logic layer. It is a way to organize your code in a way that makes it easier support for different ORM system. In this section, we will learn how to create repositories for our models.
Interfaces
To create a repository, we need to implement the repositories interface, which located in fastauth.repositories
module.
FastAuth provides a 3 base interfaces: AbstractUserRepository
, AbstractRolePermissionRepository
, and AbstractOAuthRepository
.
Repository interfaces
class AbstractUserRepository(Generic[UP, ID], ABC):
user_model: type[UP]
@abstractmethod
async def get_by_id(self, pk: ID) -> UP | None:
"""
Get user by id
:param pk: User id
:return: User model or None
"""
raise NotImplementedError
@abstractmethod
async def get_by_email(self, email: str) -> UP | None:
"""
Get user by email
:param email: User email
:return User model or None
"""
raise NotImplementedError
@abstractmethod
async def get_by_username(self, username: str) -> UP | None:
"""
Get user by email
:param username: User username
:return User model or None
"""
raise NotImplementedError
@abstractmethod
async def get_by_fields(self, value: str, fields: list[str]) -> UP | None:
"""
Get user by multiple fields and username. Just check in cycle if user.<field> == username
:param value: User field value
:param fields: list of fields on user model
:return User model or None
"""
raise NotImplementedError
@abstractmethod
async def get_by_field(self, value: Any, field: str) -> UP | None:
"""
Get user by his value in field
:param value: User model field value
:param field: User model field name
:return: User model or None
"""
raise NotImplementedError
@abstractmethod
async def create(self, data: dict[str, Any]) -> UP:
"""
Create User in DB from data dict
:param data: User model payload
:return New User model
"""
raise NotImplementedError
@abstractmethod
async def update(self, user: UP, data: dict[str, Any]) -> UP:
"""
Update provided user model with provided data
:param user: User model
:param data: Data with which updates user
:return Updated user model"""
raise NotImplementedError
@abstractmethod
async def delete(self, user: UP) -> None:
"""
Delete provided user model from db
:param user: User model
:return None
"""
raise NotImplementedError
class AbstractRolePermissionRepository(Generic[RP, PP], ABC):
role_model: type[RP]
permission_model: type[PP]
@abstractmethod
async def get_role(self, role_id: int) -> RP | None:
"""
Ger role by id
:param role_id: INTEGER Primary key
:return: Role model
"""
raise NotImplementedError
@abstractmethod
async def get_role_by_codename(self, codename: str) -> RP | None:
"""
Ger role by codename
:param codename: Role codename
:return: Role model
"""
raise NotImplementedError
@abstractmethod
async def create_role(self, data: dict[str, Any]) -> RP:
"""
Create new role in db from data dict
:param data: Role model payload
:return: New Role model
"""
raise NotImplementedError
@abstractmethod
async def update_role(self, role: RP, data: dict[str, Any]) -> RP:
"""
Update provided role model with provided data
:param role: Role model
:param data: Data with which updates role
:return: Updated role model
"""
raise NotImplementedError
@abstractmethod
async def delete_role(self, role: RP) -> None:
"""
Delete provided role model from db
:param role: Role model
:return: None
"""
raise NotImplementedError
@abstractmethod
async def list_roles(self) -> list[RP]:
"""
List all roles
:return: List of Role models
"""
raise NotImplementedError
@abstractmethod
async def get_permission(self, permission_id: int) -> PP | None:
"""
Get permission by id
:param permission_id: INTEGER Primary key
:return: Permission model
"""
raise NotImplementedError
@abstractmethod
async def get_permission_by_codename(self, codename: str) -> PP | None:
"""
Get permission by codename
:param codename: Permission codename
:return: Permission model
"""
raise NotImplementedError
@abstractmethod
async def create_permission(self, data: dict[str, Any]) -> PP:
"""
Create new permission in db from data dict
:param data: Permission model payload
:return: New Permission model
"""
raise NotImplementedError
@abstractmethod
async def update_permission(self, permission: PP, data: dict[str, Any]) -> PP:
"""
Update provided permission model with provided data
:param permission: Permission model
:param data: Data with which updates permission
:return: Updated permission model
"""
raise NotImplementedError
@abstractmethod
async def delete_permission(self, permission: PP) -> None:
"""
Delete provided permission model from db
:param permission: Permission model
:return: None
"""
raise NotImplementedError
@abstractmethod
async def list_permissions(self) -> list[PP]:
"""
List all permissions
:return: List of Permission models
"""
raise NotImplementedError
class AbstractOAuthRepository(Generic[UOAP, OAP], ABC):
user_model: type[UOAP]
oauth_model: type[OAP]
@abstractmethod
async def get_user(self, oauth_name: str, account_id: str) -> UOAP | None:
"""
Get user by oauth account name and id
:param oauth_name: OAuth client name
:param account_id: OAuthAccount model PK
:return: User model
"""
raise NotImplementedError
@abstractmethod
async def add_oauth_account(self, user: UOAP, data: dict[str, Any]) -> UOAP:
"""
Create a new OAuth account and add it to User model
:param user: User model
:param data: OAuth Account data
:return: User model
"""
raise NotImplementedError
@abstractmethod
async def update_oauth_account(
self, user: UOAP, oauth: OAP, data: dict[str, Any]
) -> UOAP:
"""
Update provided OAuth account with provided data, and refresh user in DB
:param user: User model
:param oauth: OAuthAccount model
:param data: Data with which updates OAuth account
:return: Updated User model
"""
raise NotImplementedError
Implementation
FastAuth provides already implemented repositories for supported ORMs.
If you don't see your ORM in the list, you can implement your own repository by implementing the repository interface.
To implement a repository, you need to create a class that inherits repositories implementation for specified ORMs, which
located in fastauth.contrib.<ORM>.repositories
module.
You need pass model class to repository Generic for type hinting, and also set class attribute with suffix model
to the model class.
For UserRepository, set user_model
, for RBACRepository set role_model
and permission_model
, and for OAuthRepository set user_model
and oauth_model
.
See example below:
SQLAlchemy Repository implementation
from fastauth.contrib.sqlalchemy import repositories
import uuid
class UserRepository(repositories.SQLAlchemyUserRepository[User, uuid.UUID]):
user_model = User
class RBACRepository(repositories.SQLAlchemyRBACRepository[Role, Permission]):
role_model = Role
permission_model = Permission
class OAuthRepository(repositories.SQLAlchemyOAuthRepository[User, OAuthAccount]):
user_model = User
oauth_model = OAuthAccount