141 lines
3.6 KiB
Python
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)
|