Using reserved keywords or built-in functions as variable/attribute names is commonly seen as bad practice.
However, the SQLALchemy tutorial is full of exampled with attributes named id
.
Straight from the tutorial
>>> class User(Base):
... __tablename__ = "user_account"
...
... id: Mapped[int] = mapped_column(primary_key=True)
... name: Mapped[str] = mapped_column(String(30))
... fullname: Mapped[Optional[str]]
...
... addresses: Mapped[List["Address"]] = relationship(
... back_populates="user", cascade="all, delete-orphan"
... )
...
... def __repr__(self) -> str:
... return f"User(id={self.id!r}, name={self.name!r}, fullname={self.fullname!r})"
Why is it not recommended to use id_
instead, as recommended at least for keywords in PEP 8?
Firstly, id
is not a keyword; if it was one, you would get a syntax error (try replacing id
there with e.g. pass
). It is a global identifier for a built-in function. And while it is a bad practice to clobber over the default identifiers defined by Python, note that id: Mapped[int] = mapped_column(primary_key=True)
inside a class definition defines a static attribute of that class, not a global variable. The global function id
is not the same as the static attribute User.id
(or self.id
, as it is used later) — there is no conflict there.
class Foo:
id = 42
print(Foo.id)
# => 42
print(id)
# => <built-in function id>
However, as you correctly note:
id = "YOU SHOULDN'T DO THIS"
print(id)
# => YOU SHOULDN'T DO THIS
# (and not <built-in function id>)
Secondly, as to why id
is used: SQLAlchemy maps classes to SQL tables with the snakecased class name, and attributes to SQL columns with the as-is attribute name (unless you specifically say id_: mapped_column('id', primary_key=True)
), so id
and id_
would be different columns in SQL.
id
(as opposed to id_
) is the conventional name of the primary key column in SQL (if there is a single-column primary key). Some people would name the column user_id
, but many dislike having user_id
in table called users
, since it should be obvious what users.id
, and so users.user_id
is redundant; the form user_id
is generally reserved for foreign keys. The SQLAlchemy examples merely follow this SQL convention.