Nord.Kind
Nord.Kind

Reputation: 101

Difference and benefit of using Declarative Base Classes instead of table objects in SQLAlchemy

I am having problems understanding the benefit of the usage of declarative classes in SQLAlchemy.

As I understand the ORM is a way to apply the concept of database tables to the class system of OOP. However I don't understand why the table class doesn't already satisfy this requirement.

So to form my question via an example:

What is the benefit of using this:

from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
from sqlalchemy import Column, Integer, String
class User(Base):
     __tablename__ = 'users'

     id = Column(Integer, primary_key=True)
     name = Column(String(16))
     fullname = Column(String(60))
     nickname = Column(String(50))

Instead of this:

from sqlalchemy import *
metadata = MetaData()
user = Table('users', metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String(16)),
    Column('fullname ', String(60)),
    Column('nickname ', String(50))
)

The latter one is already a class representation, isn't it? Why are we building another class over the already existing table class? What's the benefit?

Upvotes: 1

Views: 2185

Answers (2)

abdelrahman aboneda
abdelrahman aboneda

Reputation: 790

You can say DeclarativeBase is anther layer on top of Table that has those Benefits:

1 - Simplicity and Readability
Declarative Mapping combines table definitions and class behavior into one place, reducing boilerplate code.

Example (Declarative Base):

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    username = Column(String, unique=True)
    email = Column(String, unique=True)

Classical Mapping Equivalent:

users_table = Table(
    'users', metadata,
    Column('id', Integer, primary_key=True),
    Column('username', String, unique=True),
    Column('email', String, unique=True)
)

class User:
    pass

mapper(User, users_table)
  1. Integrated Relationships Relationships (relationship) and foreign keys are defined directly in the class, keeping all model-related information centralized.

Declarative Example:

class Post(Base):
    __tablename__ = 'posts'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('users.id'))
    user = relationship('User', back_populates='posts')

Classical Mapping Equivalent:

posts_table = Table(
    'posts', metadata,
    Column('id', Integer, primary_key=True),
    Column('user_id', Integer, ForeignKey('users.id'))
)

mapper(Post, posts_table, properties={
    'user': relationship(User, back_populates='posts')
})

3- Extensibility Declarative Base allows easy extension through mixins and custom attributes.

class TimestampMixin:
    created_at = Column(DateTime, default=datetime.utcnow)
    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

class User(Base, TimestampMixin):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    username = Column(String, unique=True)

Upvotes: 0

Archi
Archi

Reputation: 51

I have the same question recently, you can refer to SQLAlchemy doc.

Some examples in the documentation still use the classical approach, but note that the classical as well as Declarative approaches are fully interchangeable.

I think the benefit of using Declarative Mapping is that,

  1. more convinent to use foreign key.
  2. when you want to create table with some table_args/mapper_args, just write in all down in the class.

Upvotes: 3

Related Questions