SQLAlchemy - UUID and Mix-In Classes
UUID
UUID (Universal Unique Identifier) is a 128-bit value used to uniquely identify an object or entity on the internet.
A real world example; When you visit a news website, you see something following in the address bar of the web browser.
https://newssite.example/category/economy/1
As you can imagine, the 1
indicates that it's the first article. If you increase the number you view the second article. It's predictable and the people can copy your content easily, just by increasing the numbers. So hiding your content's identical number to visitors can be crucial for web services. To be able to hide it you can start to implement UUID instead of regular numbers. Predicting UUID is difficult but implementing it is easy. Because an UUID looks like below;291ad1cd-c352-4220-b8f8-5ee876da5390
If you can guess the next article's ID, don't wait and play lottery :)
The best place to put UUID field is the Base class. We ensure all resources in database has UUID type by defining there. And it is good for DRY (don't repeat yourself) principle.
As default parameter, we provide uuid.uuid4
function's itself. SQLAlchemy will call the function to generate UUID values. Both way is shown in below.
from sqlalchemy.orm import as_declarative
from sqlalchemy import Column, Integer
from sqlalchemy.dialects.postgresql import UUID
import uuid
class_registry: dict = {}
@as_declarative(class_registry=class_registry)
class Base():
# id = Column('id', Integer, primary_key=True, index=True)
uuid = Column('uuid', UUID(as_uuid=True), primary_key=True, index=True, default=uuid.uuid4)
Auto-Generated Table Names
Instead of typing __tablename__
in each model classes, you can follow below approach in Base class. You can use this decorator for more of course, like creating Mix-in classes. (in below it's explained)
from sqlalchemy.orm import as_declarative, declared_attr
from sqlalchemy import Column
from sqlalchemy.dialects.postgresql import UUID
import uuid
class_registry: dict = {}
@as_declarative(class_registry=class_registry)
class Base():
@declared_attr
def __tablename__(cls):
return f"{cls.__name__.lower()}s"
uuid = Column('uuid', UUID(as_uuid=True), primary_key=True, index=True, default=uuid.uuid4)
Common Date Time Mix-in Classes
For auditing purposes and keeping everything under record, we should keep the creation and update dates. The below mix-in class is our abstract class. We will use them in classes which needs Date Time records.
Creation date and update date can be set automatically. Be aware of that we use server_default
in created_at
with func.now()
But we use onupdate
in updated_at
again with func.now()
The reason is explained in this thread.
from sqlalchemy import DateTime, func
from sqlalchemy.orm import declared_attr
class DateTimeMixin:
@declared_attr
def created_at(cls):
return Column(DateTime(timezone=True), server_default=func.now())
@declared_attr
def updated_at(cls):
return Column(DateTime(timezone=True), onupdate=func.now())
class User(Base, DateTimeMixin):
__tablename__ = "users"
username = Column(String, index=True)
password = Column(String)