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)