Skip to content

added additional fields #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions fastapi_users_tortoise/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
"""FastAPI Users database adapter for Tortoise ORM."""
from typing import Any, Dict, Generic, Optional, Type, TypeVar, cast
import re
from uuid import UUID

from fastapi_users.db.base import BaseUserDatabase
from fastapi_users.models import ID, OAP
from tortoise import fields, models
from tortoise.exceptions import DoesNotExist
from tortoise.exceptions import DoesNotExist, ValidationError
from tortoise.timezone import now
from tortoise.validators import RegexValidator

__version__ = "0.2.0"


class UsernameValidator(RegexValidator):
def __init__(self):
pattern = r"^[\w.@+-]+\Z"
super().__init__(pattern, 0)

def __call__(self, value: Any):
if not self.regex.match(value):
raise ValidationError(
"Username must only container letters, numbers, and @/./+/-/_ characters."
)


class TortoiseBaseUserAccountModel(models.Model):
"""Base Tortoise ORM users model definition."""

Expand All @@ -19,12 +34,36 @@ class User(TortoiseBaseUserAccountModel[uuid.UUID]):
so using generics here to specify the id ID is pointless.
"""

username_validator = UsernameValidator()

id: Any
email: str = fields.CharField(index=True, unique=True, null=False, max_length=255)
first_name = fields.CharField(null=True, max_length=150)
last_name = fields.CharField(null=True, max_length=150)
username = fields.CharField(
null=True, unique=True, max_length=50, validators=[username_validator]
)
email: str = fields.CharField(
index=True,
unique=True,
null=False,
max_length=255,
)
hashed_password: str = fields.CharField(null=False, max_length=1024)
is_active = fields.BooleanField(default=True, null=False)
is_superuser = fields.BooleanField(default=False, null=False)
is_verified = fields.BooleanField(default=False, null=False)
date_joined = fields.DatetimeField(default=now)

def full_name(self):
"""
return first_name and last_name
"""
full_name = f"{self.first_name} {self.last_name}"
return full_name.strip()

class PydanticMeta:
computed = ["full_name"]
exclude = ["hashed_password"]

class Meta:
abstract = True
Expand Down
34 changes: 33 additions & 1 deletion tests/test_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import pytest
from tortoise import Tortoise, fields
from tortoise.exceptions import ValidationError

from fastapi_users_tortoise import (
TortoiseBaseUserAccountModelUUID,
Expand Down Expand Up @@ -37,7 +38,9 @@ async def tortoise_user_db() -> AsyncGenerator[TortoiseUserDatabase[User, UUID],


@pytest.fixture
async def tortoise_user_db_oauth() -> AsyncGenerator[TortoiseUserDatabase[User, UUID], None]:
async def tortoise_user_db_oauth() -> AsyncGenerator[
TortoiseUserDatabase[User, UUID], None
]:
DATABASE_URL = "sqlite://./test-tortoise-user-oauth.db"

await Tortoise.init(
Expand Down Expand Up @@ -68,10 +71,22 @@ async def test_queries(
assert user.is_active is True
assert user.is_superuser is False
assert user.email == user_create["email"]
assert user.first_name is None
assert user.last_name is None
assert user.username is None
assert user.date_joined is not None

# Update
updated_user = await tortoise_user_db.update(user, {"is_superuser": True})
assert updated_user.is_superuser is True

# update first and last name
first_name = "Lancelot"
last_name = "Camelot"
updated_user = await tortoise_user_db.update(user, {"first_name": first_name, "last_name": last_name})
assert updated_user.first_name == first_name
assert updated_user.last_name == last_name
assert updated_user.full_name() == f"{first_name} {last_name}"

# Get by id
id_user = await tortoise_user_db.get(user.id)
Expand Down Expand Up @@ -236,3 +251,20 @@ async def test_queries_oauth(
# Unknown OAuth account
unknown_oauth_user = await tortoise_user_db_oauth.get_by_oauth_account("foo", "bar")
assert unknown_oauth_user is None


@pytest.mark.asyncio
async def test_username_validation(tortoise_user_db: TortoiseUserDatabase[User, UUID]):
user_create = {
"username": "lancee007",
"email": "lancelot@camelot.bt",
"hashed_password": "guinevere",
}

# Create
user = await tortoise_user_db.create(user_create)
assert user.username == user_create["username"]

# update to invalid
with pytest.raises(ValidationError):
await tortoise_user_db.update(user, {"username": "lance&lot"})