Update docs for SQLAlchemy 2.0

This commit is contained in:
François Voron
2023-02-13 17:27:16 +01:00
parent 15c60525d4
commit 5c48283155
10 changed files with 57 additions and 39 deletions

View File

@@ -21,7 +21,7 @@ We are providing a base model with those fields for each database we are support
We'll expand from the basic SQLAlchemy configuration.
```py hl_lines="5-8 21-22 43-46"
```py hl_lines="5-8 23-24 45-48"
--8<-- "docs/src/db_sqlalchemy_access_tokens.py"
```
@@ -37,8 +37,8 @@ We'll expand from the basic SQLAlchemy configuration.
```py
class AccessToken(SQLAlchemyBaseAccessTokenTable[int], Base):
@declared_attr
def user_id(cls):
return Column(Integer, ForeignKey("user.id", ondelete="cascade"), nullable=False)
def user_id(cls) -> Mapped[int]:
return mapped_column(Integer, ForeignKey("user.id", ondelete="cascade"), nullable=False)
```
Notice that `SQLAlchemyBaseAccessTokenTable` expects a generic type to define the actual type of ID you use.

View File

@@ -20,7 +20,7 @@ For the sake of this tutorial from now on, we'll use a simple SQLite database.
As for any SQLAlchemy ORM model, we'll create a `User` model.
```py hl_lines="13-14"
```py hl_lines="15-16"
--8<-- "docs/src/db_sqlalchemy.py"
```
@@ -31,7 +31,7 @@ As you can see, **FastAPI Users** provides a base class that will include base f
```py
class User(SQLAlchemyBaseUserTable[int], Base):
id = Column(Integer, primary_key=True)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
```
Notice that `SQLAlchemyBaseUserTable` expects a generic type to define the actual type of ID you use.
@@ -40,7 +40,7 @@ As you can see, **FastAPI Users** provides a base class that will include base f
We'll now create an utility function to create all the defined tables.
```py hl_lines="21-23"
```py hl_lines="23-25"
--8<-- "docs/src/db_sqlalchemy.py"
```
@@ -53,7 +53,7 @@ This function can be called, for example, during the initialization of your Fast
The database adapter of **FastAPI Users** makes the link between your database configuration and the users logic. It should be generated by a FastAPI dependency.
```py hl_lines="26-33"
```py hl_lines="28-34"
--8<-- "docs/src/db_sqlalchemy.py"
```

View File

@@ -32,11 +32,11 @@ google_oauth_client = GoogleOAuth2("CLIENT_ID", "CLIENT_SECRET")
You'll need to define the SQLAlchemy model for storing OAuth accounts. We provide a base one for this:
```py hl_lines="5 17-18 22 39-40"
```py hl_lines="5 19-20 24-26 43-44"
--8<-- "docs/src/db_sqlalchemy_oauth.py"
```
Notice that we also manually added a `relationship` on the `UserTable` so that SQLAlchemy can properly retrieve the OAuth accounts of the user.
Notice that we also manually added a `relationship` on `User` so that SQLAlchemy can properly retrieve the OAuth accounts of the user.
Besides, when instantiating the database adapter, we need pass this SQLAlchemy model as third argument.
@@ -45,11 +45,11 @@ Besides, when instantiating the database adapter, we need pass this SQLAlchemy m
```py
class OAuthAccount(SQLAlchemyBaseOAuthAccountTable[int], Base):
id = Column(Integer, primary_key=True)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
@declared_attr
def user_id(cls):
return Column(Integer, ForeignKey("user.id", ondelete="cascade"), nullable=False)
def user_id(cls) -> Mapped[int]:
return mapped_column(Integer, ForeignKey("user.id", ondelete="cascade"), nullable=False)
```

View File

@@ -2,11 +2,14 @@ from typing import AsyncGenerator
from fastapi import Depends
from fastapi_users.db import SQLAlchemyBaseUserTableUUID, SQLAlchemyUserDatabase
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import DeclarativeMeta, declarative_base, sessionmaker
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase
DATABASE_URL = "sqlite+aiosqlite:///./test.db"
Base: DeclarativeMeta = declarative_base()
class Base(DeclarativeBase):
pass
class User(SQLAlchemyBaseUserTableUUID, Base):
@@ -14,7 +17,7 @@ class User(SQLAlchemyBaseUserTableUUID, Base):
engine = create_async_engine(DATABASE_URL)
async_session_maker = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async_session_maker = async_sessionmaker(engine, expire_on_commit=False)
async def create_db_and_tables():

View File

@@ -6,11 +6,14 @@ from fastapi_users_db_sqlalchemy.access_token import (
SQLAlchemyAccessTokenDatabase,
SQLAlchemyBaseAccessTokenTableUUID,
)
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import DeclarativeMeta, declarative_base, sessionmaker
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase
DATABASE_URL = "sqlite+aiosqlite:///./test.db"
Base: DeclarativeMeta = declarative_base()
class Base(DeclarativeBase):
pass
class User(SQLAlchemyBaseUserTableUUID, Base):
@@ -22,7 +25,7 @@ class AccessToken(SQLAlchemyBaseAccessTokenTableUUID, Base): # (1)!
engine = create_async_engine(DATABASE_URL)
async_session_maker = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async_session_maker = async_sessionmaker(engine, expire_on_commit=False)
async def create_db_and_tables():

View File

@@ -6,11 +6,14 @@ from fastapi_users.db import (
SQLAlchemyBaseUserTableUUID,
SQLAlchemyUserDatabase,
)
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import DeclarativeMeta, declarative_base, relationship, sessionmaker
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, relationship
DATABASE_URL = "sqlite+aiosqlite:///./test.db"
Base: DeclarativeMeta = declarative_base()
class Base(DeclarativeBase):
pass
class OAuthAccount(SQLAlchemyBaseOAuthAccountTableUUID, Base):
@@ -18,11 +21,13 @@ class OAuthAccount(SQLAlchemyBaseOAuthAccountTableUUID, Base):
class User(SQLAlchemyBaseUserTableUUID, Base):
oauth_accounts: List[OAuthAccount] = relationship("OAuthAccount", lazy="joined")
oauth_accounts: Mapped[List[OAuthAccount]] = relationship(
"OAuthAccount", lazy="joined"
)
engine = create_async_engine(DATABASE_URL)
async_session_maker = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async_session_maker = async_sessionmaker(engine, expire_on_commit=False)
async def create_db_and_tables():

View File

@@ -6,11 +6,14 @@ from fastapi_users.db import (
SQLAlchemyBaseUserTableUUID,
SQLAlchemyUserDatabase,
)
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import DeclarativeMeta, declarative_base, relationship, sessionmaker
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, relationship
DATABASE_URL = "sqlite+aiosqlite:///./test.db"
Base: DeclarativeMeta = declarative_base()
class Base(DeclarativeBase):
pass
class OAuthAccount(SQLAlchemyBaseOAuthAccountTableUUID, Base):
@@ -18,11 +21,13 @@ class OAuthAccount(SQLAlchemyBaseOAuthAccountTableUUID, Base):
class User(SQLAlchemyBaseUserTableUUID, Base):
oauth_accounts: List[OAuthAccount] = relationship("OAuthAccount", lazy="joined")
oauth_accounts: Mapped[List[OAuthAccount]] = relationship(
"OAuthAccount", lazy="joined"
)
engine = create_async_engine(DATABASE_URL)
async_session_maker = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async_session_maker = async_sessionmaker(engine, expire_on_commit=False)
async def create_db_and_tables():

View File

@@ -2,11 +2,14 @@ from typing import AsyncGenerator
from fastapi import Depends
from fastapi_users.db import SQLAlchemyBaseUserTableUUID, SQLAlchemyUserDatabase
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import DeclarativeMeta, declarative_base, sessionmaker
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.orm import DeclarativeBase
DATABASE_URL = "sqlite+aiosqlite:///./test.db"
Base: DeclarativeMeta = declarative_base()
class Base(DeclarativeBase):
pass
class User(SQLAlchemyBaseUserTableUUID, Base):
@@ -14,7 +17,7 @@ class User(SQLAlchemyBaseUserTableUUID, Base):
engine = create_async_engine(DATABASE_URL)
async_session_maker = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async_session_maker = async_sessionmaker(engine, expire_on_commit=False)
async def create_db_and_tables():

View File

@@ -156,7 +156,7 @@ class BaseUserManager(Generic[models.UP, models.ID]):
refresh_token: Optional[str] = None,
request: Optional[Request] = None,
*,
associate_by_email: bool = False
associate_by_email: bool = False,
) -> models.UOAP:
"""
Handle the callback after a successful OAuth authentication.

View File

@@ -63,9 +63,9 @@ def forgot_password_token(user_manager: UserManagerMock[UserModel]):
@pytest.fixture
def create_oauth2_password_request_form() -> Callable[
[str, str], OAuth2PasswordRequestForm
]:
def create_oauth2_password_request_form() -> (
Callable[[str, str], OAuth2PasswordRequestForm]
):
def _create_oauth2_password_request_form(username, password):
return OAuth2PasswordRequestForm(username=username, password=password, scope="")
@@ -252,7 +252,6 @@ class TestOAuthAssociateCallback:
user_manager_oauth: UserManagerMock[UserOAuthModel],
superuser_oauth: UserOAuthModel,
):
user = await user_manager_oauth.oauth_associate_callback(
superuser_oauth,
"service1",