Repository navigation
How to specify a different name for the db table that a SQLModel refers to? #159
Description
Activity
- addedquestionFurther information is requestedFurther information is requested
on Nov 18, 2021 I tried manually setting
__table__and__tablename__on the models, but that did not do anything (would be super hacky anyway...).Seems that setting
__tablename__as part of the class definition worked (at least, it seems to have created the table with the correct name when testing with sqlite):class Product(SQLModel, table=True): __tablename__ = "assets" id: Optional[int] = Field(None, primary_key=True) name: str
I tried this based on reading this in the sqlalchemy docs: https://docs.sqlalchemy.org/en/13/orm/extensions/declarative/table_config.html
Just to confirm though: is this the recommended way to deal with the scenario?
Reacted by Abolfazl Nasr, joaopfg, Max Corbeau, Jannis Lübbe, Mohammad Mesbah, Karl Hendrik, Masroor Ehsan, astroboy, Tony Reksoatmodjo, Tomonori Fujiwara and 18 more@taranlu-houzz SQLModel is essentially a combination of SQLAlchemy (for ORM) and Pydantic (for validation).
While not explicitly documented in SQLModel, yes you right that using the
__tablename__attribute will amend the auto generated table name for a class.You can also see in the doc tests that this attribute is asserted:
https://github.com/tiangolo/sqlmodel/search?q=__tablename__
Finally, in the main codebase, you can see that unless set otherwise, the
__tablename__attribute defaults to the class name lowercased:Reacted by Taran Lu, Magnus Markling, ChouUn, Masroor Ehsan, Jakub Červinka, Frank Odom, Sam Woolerton, Denis A and 5j9@chriswhite199 Thanks for the confirmation. I'd say my questions is resolved now.
If you're using Pylance/Pyright, you should do
__tablename__: str = name_in_database
If you don't include the
: strannotation, you'll see the errorExpression of type "
str" cannot be assigned to declared type "declared_attr"
"str" is incompatible with "declared_attr"Credit to @indivisible for #98 (comment)
Reacted by Evan E. Baird, ruibinzhang, Collins, Jesse, Adam Benali, Grant Watts, bubbletroubles, Jnn, Nikita Bilous, 净北 and 9 moreReacted by Magnus Markling, Jacob Linney, Fu YunGe, FallenChromium, Trond Hauklien, Cristian Piersinaru, Jonatas Leon, Wayde Gilliam, Joshua Szepietowski, Matías Barra and 17 moreA very convenient way to change the table name generation is overriding the
__tablename__class method. E.g. to convert the class name to snake casing:from sqlmodel import SQLModel as _SQLModel from sqlalchemy.orm import declared_attr from .utils import snake_case class SQLModel(_SQLModel): @declared_attr def __tablename__(cls) -> str: return snake_case(cls.__name__) class HTTPProxy(SQLModel): ... print(HTTPProxy.__tablename__) #> http_proxy
# utils.py from functools import partial import re _snake_1 = partial(re.compile(r'(.)((?<![^A-Za-z])[A-Z][a-z]+)').sub, r'\1_\2') _snake_2 = partial(re.compile(r'([a-z0-9])([A-Z])').sub, r'\1_\2') def snake_case(string: str) -> str: return _snake_2(_snake_1(string)).casefold()
A
SQLModel.Config.tablename_generatorprop that accepts a callable (like Pydantic'sBaseModel.Config.alias_generator) would be great feature.Reacted by Masroor Ehsan, anci3ntr0ck, Luiggi Mendoza, Paul Stoker, Stan, helloWorld74323, Henry (Duc) Bui, Venkatesh-Prasad Ranganath and Martin O'LearyReacted by Juan Felipe Monsalve, Vitor Schirmer, Stan, Bill, Riyad Morshed Shoeb and Martin O'LearyReacted by Bill, thehappycheese and Martin O'LearyIf you're using Pylance/Pyright, you should do
__tablename__: str = name_in_databaseI'm using Pyright and this doesn't work. When I try it I get the error
"__tablename__" incorrectly overrides property of same name in class "SQLModel" Pylance(reportIncompatibleMethodOverride)Not sure why this is happening to me but not others. Perhaps I'm on a newer version of Pyright. There are two fixes. First, use
declared_attr:class Lineitem(SQLModel, table=True): @declared_attr def __tablename__(cls): return "invoice_lineitems"
Second, just tell Pyright to ignore it:
__tablename__: str = ( # pyright: ignore [reportIncompatibleMethodOverride] "invoices_legacy" )
or just:
__tablename__: str = "invoices_legacy" # type: ignore
I choose this last method because the others are far too verbose for something as simple as declaring the table name. This may seem hacky, but although I have to tell Pyright to ignore a number of things in sqlmodel, it's not half the file like I have to
type: ignorewith sqlalchemy.As I mentioned in #98 (comment), a more intuitive solution that would make Pyright happy might be putting the table name declaration in a
Metaas ormar does:class Lineitem(SQLModel, table=True): class Meta: tablename: str = "invoice_lineitems"
Reacted by Jonas Manthey, H0nGzA1, Gaurish Gangwar, Zheng Yang, Yangwook Ian Jeong, Collins, 🦦, Jonathan Kinred, iwannarocks, Po and 2 moreI tried
class SQLModel(_SQLModel): @declared_attr def __tablename__(cls) -> str: return snake_case(cls.__name__)
But
mypycomplained thatSignature of "__tablename__" incompatible with supertype "SQLModel"Reacted by Brendan Maguire, Rômulo Gadelha, Igor Klimer and Kyle Li@kingyue737 In the base code, typing is ignored.
class SQLModel(_SQLModel): @declared_attr # type: ignore def __tablename__(cls) -> str: return snake_case(cls.__name__)
Reacted by Yue JIN and Vitor SchirmerWhat do you think of using the Pascal case instead lowercase or snake case? It can be more intuitive on foreign_key due to already class name is in pascal and cruds are in pascal either.
class SQLModel(_SQLModel): @declared_attr # type: ignore def __tablename__(cls) -> str: return cls.__name__ class RoleBase(SQLModel): name: str description: str class Role(RoleBase, table=True): id: uuid_pkg.UUID = Field(default_factory=uuid4, primary_key=True, index=True, nullable=False) users: List["User"] = Relationship(back_populates="role", sa_relationship_kwargs={"lazy": "selectin"}) created_by_id: Optional[UUID] = Field(default=None, foreign_key="User.id") created_by: "User" = Relationship(sa_relationship_kwargs={"lazy":"selectin", "primaryjoin":"Role.created_by_id==User.id"}) class UserBase(SQLModel): first_name: str last_name: str email: EmailStr class User(UserBase, table=True): id: uuid_pkg.UUID = Field(default_factory=uuid4, primary_key=True, index=True, nullable=False) role_id: Optional[UUID] = Field(default=None, foreign_key="Role.id") role: Optional["Role"] = Relationship(back_populates="users", sa_relationship_kwargs={"lazy": "selectin"})
users = await db_session.execute(select(User).where(User.email == email))
Thanks for the help and discussion here everyone! 👏 🙇
Thanks for reporting back and closing the issue @taranlu-houzz 👍
Reacted by Chris Dare, Tony Reksoatmodjo and Kleber Daniel Mattos VianaI know this is closed already but just in case anyone else winds up here-- this worked without any
type: ignorecomments for me__tablename__: typing.ClassVar[ typing.Union[str, typing.Callable[..., str]] ] = "your_table_name"
Reacted by Colin Marquardt, Petr Gazarov, Daniel Gafni and LeoReacted by Brendan Maguire, Kyle Li and rodrigo-numerafrom typing import Optional from sqlmodel import SQLModel, Field # If the actual db table is called `assets`, for example, how can I keep the following model named `Product` but have it refer to the correct table? class Product(SQLModel, table=True): __tablename__ = "product" id: Optional[int] = Field(None, primary_key=True) name: str
If someone is trying to change
__tablename__instead of creating a subclass from Product if the schema is same, then can use the following way to do it.Product.__table__.name = "custom_table_name"Reacted by Dmitry Mazur, La Min Ko and 浅小沫I know setting the table name through
__tablename__does the trick, but it is very much not like using pydantic.Is it possible to replicate the
Configin Pydantic so that we can pass extra parameters related to the database? That way, we can create a Config inner class to which we can add any configuration we need that is not supported by Pydantic, like the table name. Seems like a weird mix to be able to treat the objects as pedantic objects but at the same time have these old-looking APIs from SQLalchemy.If that is something you would be willing to consider, I could try to take a look and see how hard it would be to implement.
Reacted by Igor Iunash, Riccardo Bergamaschi, Samy Chouiti, Kevin Hu, Brendan Maguire, Peter, James Tanner, Dawson Pleasant, Ken, Tushar and 15 moreI know setting the table name through
__tablename__does the trick, but it is very much not like using pydantic.Is it possible to replicate the
Configin Pydantic so that we can pass extra parameters related to the database? That way, we can create a Config inner class to which we can add any configuration we need that is not supported by Pydantic, like the table name. Seems like a weird mix to be able to treat the objects as pedantic objects but at the same time have these old-looking APIs from SQLalchemy.If that is something you would be willing to consider, I could try to take a look and see how hard it would be to implement.
does it implement?
I know this is closed already but just in case anyone else winds up here-- this worked without any
type: ignorecomments for me__tablename__: typing.ClassVar[ typing.Union[str, typing.Callable[..., str]] ] = "your_table_name"
sorry, for me your version does not work:
├╴ "__tablename__" overrides symbol of same name in class "SQLModel" │ Variable is mutable so its type is invariant │ Override type "str | ((...) -> str)" is not the same as base type "declared_attr[Unknown]" basedpyright >> (reportIncompatibleVariableOverride) [31, 5]Reacted by Kurund Jalmi, JakePi3, Benjamin Trom, Precious , Emil Relecto and Philipp Schonevillein jpa I have two entities.
one has entity has two field: bess_id and data_type_name
the other entity named BessAttr has a promary key identifier
the data_type_name is an fk to identifier
how would you name a jpa repository to get a list of BessAttr by bess_id and what repository will you use the BessAttr repossitory or the Bess Repositoryhttps://sqlmodel.tiangolo.com/db-to-code/?h=table+name#sql-table-names
Documentation still not updated. Doesfrom sqlmodel import SQLModel, Field # If the actual db table is called `assets`, for example, how can I keep the following model named `Product` but have it refer to the correct table? class Product(SQLModel, table=True): __tablename__ = "assets" id: int | None = Field(None, primary_key=True) name: str
correct use?
Reacted by Ashraf Haress and Gus P
First Check
Commit to Help
Example Code
Description
Operating System
Linux, macOS
Operating System Details
No response
SQLModel Version
0.0.4
Python Version
3.10.0
Additional Context
No response