PySimpleSocial/orm/users.py

141 lines
3.6 KiB
Python

"""
User relation
"""
from typing import Any
from piccolo.utils.pydantic import create_pydantic_model
from piccolo.table import Table
from piccolo.columns import (
ForeignKey,
Varchar,
BigSerial,
UUID,
Date,
OnDelete,
OnUpdate,
Boolean,
Email,
Bytea,
Column,
)
from piccolo.columns.defaults.date import DateNow
from .media import Media, PublicMediaModel
class User(Table, tablename="users"):
internal_id = BigSerial(null=False, secret=True)
public_id = UUID(primary_key=True)
first_name = Varchar(length=64, null=False)
last_name = Varchar(length=64, null=True)
email_address = Email(secret=True, null=False)
username = Varchar(length=32, null=False, unique=True)
password_hash = Bytea(null=False, secret=True)
profile_picture = ForeignKey(
references=Media,
on_delete=OnDelete.set_null,
on_update=OnUpdate.cascade,
null=True,
default=None,
)
creation_date = Date(secret=True, default=DateNow(), null=False)
bio = Varchar(length=4096, null=True, default=None)
restricted = Boolean(default=False, null=False)
email_verified = Boolean(default=False, null=False, secret=True)
verified_account = Boolean(default=False, null=False)
deleted = Boolean(default=False, null=False)
locale = Varchar(length=12, default="en_US", null=False, secret=True)
UserModel = create_pydantic_model(
User,
nested=True,
)
# This madness is needed because we need to exclude
# some fields from our public API responses
PublicUserModelInternal = create_pydantic_model(
User,
nested=True,
exclude_columns=(
User.internal_id,
User.deleted,
User.restricted,
User.locale,
User.email_verified,
User.email_address,
User.password_hash,
User.creation_date,
),
model_name="PublicUser",
)
PrivateUserModelInternal = create_pydantic_model(
User,
nested=True,
exclude_columns=(User.internal_id, User.password_hash, User.deleted),
model_name="PrivateUser",
)
class PublicUserModel(PublicUserModelInternal):
profile_picture: PublicMediaModel | None
class PrivateUserModel(PrivateUserModelInternal):
profile_picture: PublicMediaModel | None
async def get_user_by_column(
column: Column,
data: Any,
include_secrets: bool = False,
restricted_ok: bool = False,
deleted_ok: bool = False,
) -> UserModel | None:
"""
Retrieves a user object by a given criteria.
Returns None if the user doesn't exist or
if it's restricted/deleted (unless restricted_ok
and deleted_ok are set accordingly)
"""
user = (
await User.select(
*User.all_columns(),
exclude_secrets=not include_secrets,
)
.where(column == data)
.first()
)
if user:
# Performs validation
user = UserModel(**user)
if (user.deleted and not deleted_ok) or (user.restricted and not restricted_ok):
return
return user
return
async def get_user_by_id(public_id: UUID, *args, **kwargs) -> UserModel:
"""
Retrieves a user by its public ID
"""
return await get_user_by_column(User.public_id, public_id, *args, **kwargs)
async def get_user_by_username(username: str, *args, **kwargs) -> UserModel | None:
"""
Retrieves a user by its public username
"""
return await get_user_by_column(User.username, username, *args, **kwargs)
async def get_user_by_email(email: str, *args, **kwargs) -> UserModel | None:
"""
Retrieves a user by its email address
"""
return await get_user_by_column(User.email_address, email, *args, **kwargs)