igorbenav / FastAPI-boilerplate

An extendable async API using FastAPI, Pydantic V2, SQLAlchemy 2.0, PostgreSQL and Redis.
MIT License
589 stars 69 forks source link

Cannot create new model in database #130

Closed vnlebaoduy closed 5 months ago

vnlebaoduy commented 5 months ago

Problem 1: When i create a new model, but when i run docker-compose. It's not exists in my database.

from datetime import UTC, datetime
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy import DateTime, ForeignKey, String,Integer
from ..core.db.database import Base

class Template(Base):
    __tablename__ = "template"

    id: Mapped[int] = mapped_column("id", autoincrement=True, nullable=False, unique=True, primary_key=True, init=False)
    text: Mapped[str] = mapped_column(String)
    description: Mapped[str] = mapped_column(String)
    created_by: Mapped[int] = mapped_column(Integer, nullable=False)
    updated_by: Mapped[int] = mapped_column(Integer, nullable=False)
    deleted_by: Mapped[int] = mapped_column(Integer, nullable=False)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default_factory=lambda: datetime.now(UTC))
    updated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), default=None)
    deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), default=None)
    is_deleted: Mapped[bool] = mapped_column(default=False, index=True)

Screenshots CleanShot 2024-04-10 at 00 11 33@2x

Problem 2: When i migrating, my database has been delete all table. This is log migration

poetry run alembic revision --autogenerate
INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.ddl.postgresql] Detected sequence named 'rate_limit_id_seq' as owned by integer column 'rate_limit(id)', assuming SERIAL and omitting
INFO  [alembic.autogenerate.compare] Detected removed index 'ix_rate_limit_tier_id' on 'rate_limit'
INFO  [alembic.autogenerate.compare] Detected removed table 'rate_limit'
INFO  [alembic.ddl.postgresql] Detected sequence named 'token_blacklist_id_seq' as owned by integer column 'token_blacklist(id)', assuming SERIAL and omitting
INFO  [alembic.autogenerate.compare] Detected removed index 'ix_token_blacklist_token' on 'token_blacklist'
INFO  [alembic.autogenerate.compare] Detected removed table 'token_blacklist'
INFO  [alembic.ddl.postgresql] Detected sequence named 'post_id_seq' as owned by integer column 'post(id)', assuming SERIAL and omitting
INFO  [alembic.autogenerate.compare] Detected removed index 'ix_post_created_by_user_id' on 'post'
INFO  [alembic.autogenerate.compare] Detected removed index 'ix_post_is_deleted' on 'post'
INFO  [alembic.autogenerate.compare] Detected removed table 'post'
INFO  [alembic.autogenerate.compare] Detected removed index 'ix_user_email' on 'user'
INFO  [alembic.autogenerate.compare] Detected removed index 'ix_user_is_deleted' on 'user'
INFO  [alembic.autogenerate.compare] Detected removed index 'ix_user_tier_id' on 'user'
INFO  [alembic.autogenerate.compare] Detected removed index 'ix_user_username' on 'user'
INFO  [alembic.autogenerate.compare] Detected removed table 'user'
INFO  [alembic.autogenerate.compare] Detected removed table 'tier'
  Generating /src/migrations/versions/d02f4a524850_.py ...  done
igorbenav commented 5 months ago

This is probably happening because you (and I'm supposing, so correct me if I'm wrong) are creating the model, but not importing it anywhere.

If you create the model but don't use it anywhere, SQLAlchemy isn't aware that it exists. To fix this you can just create the Template model's CRUD with FastCRUD or you can just import the Template model in src/app/core/setup.py and SQLAlchemy will do the rest.

vnlebaoduy commented 5 months ago

If I add the app/core/setup.py file, the database can be created, but that's not sound good.

I have already created the model file, schema file, and curd file. But they are still not initialized in the database. I don't know if I'm missing any other steps? And do I have to add the curd file somewhere so it can initialize the table in the database?

This is my file

Model File

from datetime import UTC, datetime
from sqlalchemy.dialects.postgresql import JSON
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy import DateTime, ForeignKey, String,Integer
from ..core.db.database import Base

class EncryptTemplate(Base):
    __tablename__ = "encrypt_template"

    id: Mapped[int] = mapped_column("id", autoincrement=True, nullable=False, unique=True, primary_key=True, init=False)
    text: Mapped[str] = mapped_column(String)
    description: Mapped[str] = mapped_column(String)
    created_by: Mapped[int] = mapped_column(Integer, nullable=False)
    updated_by: Mapped[int] = mapped_column(Integer, nullable=False)
    deleted_by: Mapped[int] = mapped_column(Integer, nullable=False)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default_factory=lambda: datetime.now(UTC))
    updated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), default=None)
    deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), default=None)
    is_deleted: Mapped[bool] = mapped_column(default=False, index=True)

Schema File

from datetime import datetime
from typing import Annotated, Optional
from pydantic import BaseModel, Field

class EncryptTemplateBase(BaseModel):
    text: Annotated[str, Field(examples=["Example text"])]
    description: Annotated[str, Field(examples=["Example description for template"])]

class EncryptTemplateCreate(EncryptTemplateBase):
    created_by: Annotated[int, Field(...)]  
    updated_by: Annotated[int, Field(...)]  
    deleted_by: Annotated[int, Field(...)]  

class EncryptTemplateRead(EncryptTemplateBase):
    id: Annotated[int, Field(...)]  
    created_at: datetime
    updated_at: Optional[datetime] = None
    deleted_at: Optional[datetime] = None
    is_deleted: bool

class EncryptTemplateUpdate(BaseModel):
    text: Optional[str] = None
    description: Optional[str] = None
    updated_by: Optional[int] = None
    updated_at: Optional[datetime] = Field(default_factory=datetime.utcnow)

class EncryptTemplateDelete(BaseModel):
    deleted_by: int
    deleted_at: datetime = Field(default_factory=datetime.utcnow)
    is_deleted: bool = Field(default=True)

CURD File

from fastcrud import FastCRUD

from ..models.encrypt_template import EncryptTemplate
from ..schemas.encrypt_template import EncryptTemplateRead, EncryptTemplateCreate, EncryptTemplateUpdate, EncryptTemplateDelete

CRUDEncryptTemplate= FastCRUD[EncryptTemplate, EncryptTemplateRead, EncryptTemplateCreate, EncryptTemplateUpdate, EncryptTemplateDelete]
crud_encrypt_template = CRUDEncryptTemplate(EncryptTemplate)

Can you help me, please ? Thank you so much

igorbenav commented 5 months ago

Yeah, I just tested here and it's really not creating. As a temporary solution, if you create the endpoints, it will work, I did it like this:

# src/app/api/v1/encrypt_template.py

from fastcrud import crud_router

from ...core.db.database import async_get_db
from ...models.encrypt_template import EncryptTemplate
from ...schemas.encrypt_template import EncryptTemplateCreate, EncryptTemplateUpdate

router = crud_router(
    async_get_db, 
    EncryptTemplate, 
    EncryptTemplateCreate, 
    EncryptTemplateUpdate
)

Then pass it in the v1 __init__:

# src/app/api/v1/__init__.py

from fastapi import APIRouter

from .login import router as login_router
...
from .encrypt_template import router as encrypt_router

router = APIRouter(prefix="/v1")
router.include_router(login_router)
...
router.include_router(encrypt_router)

And the table will be created:

Screenshot 2024-04-14 at 19 47 13

I'll see if I can find a better way to do this and fix it eventually.

vnlebaoduy commented 5 months ago

@igorbenav I got it.thank you so much. I have more a question about get multi_joined. When i joined 2 table many to one. I can't not create response object in object. Example: I only create like that

"data": [
    {
      "id": 0,
      "title": "string",
      "template_id": 0,
      "description": "string",
      "created_at": "2024-04-14T16:28:01.403Z",
      "created_by": "string",
      "template_title": "Example text for encryption",
      "template_description": "Example description for encryption template"
    }
  ],

This is response that i want

"data": [
    {
      "id": 0,
      "title": "string",
      "template_id": 0,
      "description": "string",
      "created_at": "2024-04-14T16:28:01.403Z",
      "created_by": "string",
      "created_at": "2024-04-14T16:28:01.403Z",
      "created_by": "string",
      "template":{
              "title": "Example text for encryption",
              "description": "Example description for encryption template"
       }
    }
  ],

Please help me :(

igorbenav commented 5 months ago

Hey, @vnlebaoduy, can you please open an issue on fastcrud's repo specifying the version and a minimal way to reproduce the issue?