woofz / sqlmodel-basecrud

Simple package that provides base CRUD operations for your models.
MIT License
47 stars 2 forks source link

sqlalchemy.exc.InvalidRequestError: Table 'publish_channel' is already defined for this MetaData instance. Specify 'extend_existing=True' to redefine options and columns on an existing Table object. #1

Closed wanghaisheng closed 2 years ago

wanghaisheng commented 2 years ago
from datetime import datetime
from typing import Optional, List

import sqlmodel
from fastapi_amis_admin.amis.components import InputRichText, InputImage, ColumnImage
from fastapi_amis_admin.models.enums import IntegerChoices
from fastapi_amis_admin.models.fields import Field
from fastapi_user_auth.auth.models import User
from sqlalchemy import Column, String

class VideoStatus(IntegerChoices):
    unpublished = 0, '未发布'
    published = 1, '已发布'
    inspection = 2, '审核中'
    disabled = 3, '已禁用'

# Create your models here.

class BaseSQLModel(sqlmodel.SQLModel):
    id: int = Field(default=None, primary_key=True, nullable=False)

    class Config:
        use_enum_values = True

class VideoPlatformLink(sqlmodel.SQLModel, table=True):
    __tablename__ = 'publish_video_platforms'
    platform_id: Optional[int] = Field(
        default=None, foreign_key="publish_platform.id", primary_key=True
    )
    video_id: Optional[int] = Field(
        default=None, foreign_key="publish_video.id", primary_key=True
    )

class VideoChannelLink(sqlmodel.SQLModel, table=True):
    __tablename__ = 'publish_video_channels'
    channel_id: Optional[int] = Field(
        default=None, foreign_key="publish_channel.id", primary_key=True
    )
    video_id: Optional[int] = Field(
        default=None, foreign_key="publish_video.id", primary_key=True
    )

class Platform(BaseSQLModel, table=True):
    __tablename__ = 'publish_platform'
    name: str = Field(..., title='PlatformName', sa_column=Column(String(255), unique=True, index=True, nullable=False))
    videos: List["Video"] = sqlmodel.Relationship(back_populates="platforms",link_model=VideoPlatformLink)
    channels: List["Channel"] = sqlmodel.Relationship(back_populates="platform")

class Channel(BaseSQLModel, table=True):
    __tablename__ = 'publish_channel'
    extend_existing=True
    name: str = Field(title='ChannelName', sa_column=Column(String(100), unique=True, index=True, nullable=False))
    description: str = Field(default='', title='Description', amis_form_item='textarea')
    status: bool = Field(None, title='status')
    videos: List["Video"] = sqlmodel.Relationship(back_populates="channels",link_model=VideoChannelLink)
    platform_id: Optional[int] = Field(default=None, foreign_key="publish_platform.id", title='PlatformId')

    platform: Optional[Platform] = sqlmodel.Relationship(back_populates="channels")

class Video(BaseSQLModel, table=True):
    __tablename__ = 'publish_video'
    title: str = Field(title='VideoTitle', max_length=200)
    img: str = Field(None, title='VideoImage', max_length=300,
                     amis_form_item=InputImage(maxLength=1, maxSize=2 * 1024 * 1024,
                                               receiver='post:/admin/file/upload'),
                     amis_table_column=ColumnImage(width=100, height=60, enlargeAble=True))
    description: str = Field(default='', title='VideoDescription', amis_form_item='textarea')
    status: VideoStatus = Field(VideoStatus.unpublished, title='status')
    content: str = Field(..., title='VideoContent', amis_form_item=InputRichText())
    create_time: Optional[datetime] = Field(default_factory=datetime.utcnow, title='CreateTime')
    source: str = Field(default='', title='VideoSource', max_length=200)

    # channel_id: Optional[int] = Field(default=None, foreign_key="publish_channel.id", title='ChannelId')
    channels: List[Channel] = sqlmodel.Relationship(back_populates="videos",link_model=VideoChannelLink)

    platforms: List[Platform] = sqlmodel.Relationship(back_populates="videos", link_model=VideoPlatformLink)

    user_id: int = Field(default=None, foreign_key="auth_user.id", title='UserId')
    user: User = sqlmodel.Relationship()

sir try to adapt your hero team model to mine, after Specify 'extend_existing=True' it still not work

woofz commented 2 years ago

Hello @wanghaisheng! Thanks for your report :) Which kind of operation are you trying to do with the repository?

Have you tried to add this

__table_args__ = {'extend_existing': True}

just below "__tablename__" ?

wanghaisheng commented 2 years ago

let me try try to define my own model 1.one platform has many channels,one channel belong to one platform 2.one platform has many videos,one video belong to many platforms 3.one channel has many videos,one video belong to many channels

so I use two link_model to express many2many

wanghaisheng commented 2 years ago

start a new fresh codebase


from datetime import datetime
from typing import Optional, List

import sqlmodel
from fastapi_amis_admin.amis.components import InputRichText, InputImage, ColumnImage
from fastapi_amis_admin.models.enums import IntegerChoices
from fastapi_amis_admin.models.fields import Field
from fastapi_user_auth.auth.models import User
from sqlalchemy import Column, String

class VideoStatus(IntegerChoices):
    unpublished = 0, '未发布'
    published = 1, '已发布'
    inspection = 2, '审核中'
    disabled = 3, '已禁用'

# Create your models here.

class BaseSQLModel(sqlmodel.SQLModel):
    id: int = Field(default=None, primary_key=True, nullable=False)

    class Config:
        use_enum_values = True

class VideoPlatformLink(sqlmodel.SQLModel, table=True):
    __tablename__ = 'publish_video_platforms'
    platform_id: Optional[int] = Field(
        default=None, foreign_key="publish_platform.id", primary_key=True
    )
    video_id: Optional[int] = Field(
        default=None, foreign_key="publish_video.id", primary_key=True
    )

class VideoChannelLink(sqlmodel.SQLModel, table=True):
    __tablename__ = 'publish_video_channels'
    channel_id: Optional[int] = Field(
        default=None, foreign_key="publish_channel.id", primary_key=True
    )
    video_id: Optional[int] = Field(
        default=None, foreign_key="publish_video.id", primary_key=True
    )

class Platform(BaseSQLModel, table=True):
    __tablename__ = 'publish_platform'
    name: str = Field(..., title='PlatformName', sa_column=Column(String(255), unique=True, index=True, nullable=False))
    videos: List["Video"] = sqlmodel.Relationship(back_populates="platforms",link_model=VideoPlatformLink)
    channels: List["Channel"] = sqlmodel.Relationship(back_populates="platform")

class Channel(BaseSQLModel, table=True):
    __tablename__ = 'publish_channel'
    extend_existing=True
    name: str = Field(title='ChannelName', sa_column=Column(String(100), unique=True, index=True, nullable=False))
    description: str = Field(default='', title='Description', amis_form_item='textarea')
    status: bool = Field(None, title='status')
    videos: List["Video"] = sqlmodel.Relationship(back_populates="channels",link_model=VideoChannelLink)
    platform_id: Optional[int] = Field(default=None, foreign_key="publish_platform.id", title='PlatformId')

    platform: Optional[Platform] = sqlmodel.Relationship(back_populates="channels")

class Video(BaseSQLModel, table=True):
    __tablename__ = 'publish_video'
    title: str = Field(title='VideoTitle', max_length=200)
    img: str = Field(None, title='VideoImage', max_length=300,
                     amis_form_item=InputImage(maxLength=1, maxSize=2 * 1024 * 1024,
                                               receiver='post:/admin/file/upload'),
                     amis_table_column=ColumnImage(width=100, height=60, enlargeAble=True))
    description: str = Field(default='', title='VideoDescription', amis_form_item='textarea')
    status: VideoStatus = Field(VideoStatus.unpublished, title='status')
    content: str = Field(..., title='VideoContent', amis_form_item=InputRichText())
    create_time: Optional[datetime] = Field(default_factory=datetime.utcnow, title='CreateTime')
    source: str = Field(default='', title='VideoSource', max_length=200)

    channel_id: Optional[int] = Field(default=None, foreign_key="publish_channel.id", title='ChannelId')
    channels: List[Channel] = sqlmodel.Relationship(back_populates="videos",link_model=VideoChannelLink)
    platform_id: Optional[int] = Field(default=None, foreign_key="publish_platform.id", title='ChannelId')

    platforms: List[Platform] = sqlmodel.Relationship(back_populates="videos", link_model=VideoPlatformLink)

    user_id: int = Field(default=None, foreign_key="auth_user.id", title='UserId')
    user: User = sqlmodel.Relationship()

sqlalchemy.exc.InvalidRequestError: Don't know how to join to <Mapper at 0x19338da69a0; Platform>. Please use the .select_from() method to establish an explicit left side, as well as providing an explicit ON clause if not present already to help resolve the ambiguity.

woofz commented 2 years ago

Hello! I'm sorry but I was busy these last days.

I tried to replicate your problem by creating a fresh project and installing all the packages you have (fastapi_amis_admin, fastapi_user_auth) but I didn't face that problem with SQLModel and SQLModel-BaseCRUD.

I had no problem to create/read/update/delete the given models