Declaring Table Classes with Multi-Column Primary Keys in SQLAlchemy

2024-07-27

  1. Define the Table Class:

  2. Declare Columns:

  3. Set Up Composite Primary Key:

Here's an example:

from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
  __tablename__ = 'users'

  # Multi-column primary key
  user_id = Column(Integer, primary_key=True)
  username = Column(String(50), primary_key=True)
  email = Column(String(100))

engine = create_engine('sqlite:///mydatabase.db')
Base.metadata.create_all(engine)

In this example, the User table has two columns, user_id and username, that together form the primary key. This means a combination of these two values must be unique to identify each user record.

  • Natural Keys: Sometimes, a combination of columns naturally defines a unique entity in your data. Composite keys reflect this real-world relationship.
  • Flexibility: They offer more control over how rows are uniquely identified compared to a single column primary key.



from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.schema import PrimaryKeyConstraint

Base = declarative_base()

class Order(Base):
  __tablename__ = 'orders'

  order_id = Column(Integer)
  product_id = Column(Integer)
  quantity = Column(Integer)

  # Define composite primary key using __table_args__
  __table_args__ = (
      PrimaryKeyConstraint('order_id', 'product_id'),
  )

engine = create_engine('sqlite:///mydatabase.db')
Base.metadata.create_all(engine)

This option defines the columns and then uses the __table_args__ attribute within the class to specify the primary key constraint. Here, PrimaryKeyConstraint is used with a tuple containing the column names.

Option 2: Using positional arguments

from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class ProductReview(Base):
  __tablename__ = 'product_reviews'

  user_id = Column(Integer, primary_key=True)
  product_id = Column(Integer, primary_key=True)
  rating = Column(Integer)

  # Define composite primary key with positional arguments
  __mapper_args__ = {
      'primary_key': [user_id, product_id]
  }

engine = create_engine('sqlite:///mydatabase.db')
Base.metadata.create_all(engine)

This option leverages the primary_key argument within the __mapper_args__ dictionary. Here, it's a list containing the column objects that form the composite key.




  1. Using PrimaryKeyConstraint class directly:

This approach utilizes the PrimaryKeyConstraint class from sqlalchemy.schema to explicitly define the constraint.

from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.schema import PrimaryKeyConstraint

Base = declarative_base()

class Customer(Base):
  __tablename__ = 'customers'

  customer_id = Column(Integer)
  email = Column(String(100))
  phone_number = Column(String(20))

  # Define composite primary key using PrimaryKeyConstraint
  __table_args__ = (
      PrimaryKeyConstraint(customer_id, email),
  )

engine = create_engine('sqlite:///mydatabase.db')
Base.metadata.create_all(engine)

Here, we create a PrimaryKeyConstraint instance with the desired column names within the __table_args__ attribute.

  1. Using Table construct with declarative:

While the previous examples used a class to define the table, SQLAlchemy also offers the Table construct from sqlalchemy.schema. This approach separates the table definition from the model class.

from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_Base
from sqlalchemy.schema import Table, PrimaryKeyConstraint

Base = declarative_base()

# Define the table structure
metadata = Base.metadata
users_table = Table(
  'users',
  metadata,
  Column('user_id', Integer, primary_key=True),
  Column('username', String(50), primary_key=True),
  Column('email', String(100)),
)

# Create a model class referencing the table
class User(object):
  __table__ = users_table

engine = create_engine('sqlite:///mydatabase.db')
Base.metadata.create_all(engine)

In this variation, we define the table structure using Table with the column definitions and primary key settings. Then, a separate class User inherits from object and references the users_table using the __table__ attribute.


sqlalchemy




Creating One-to-One Relationships with Declarative in SQLAlchemy

Start by defining two Python classes that represent your database tables. These classes will typically inherit from sqlalchemy...


Upsert in SQLAlchemy with PostgreSQL: Efficiency for Supported Databases

Query first, create if not found: This approach involves two steps: Query: You write a query to check if the object exists in the database based on unique identifiers like an ID or a combination of fields...


Efficiently Find Maximum Values in Your Database Tables with SQLAlchemy's func.max()

SQLAlchemy provides a func object that acts like a namespace for various SQL functions. Inside this func object, you'll find functions like avg (average), count...


Understanding Object Instance State in SQLAlchemy

InstanceState object: This object offers various attributes to determine the state. Here are some key ones: deleted: This attribute returns True if the object has been marked for deletion and False otherwise...



sqlalchemy

Leveraging External Libraries for Granular Result Set Caching in SQLAlchemy

This built-in feature caches the process of converting SQL statements into their string representation. When you execute the same query multiple times


Optimizing Memory Usage in SQLAlchemy Loops: When to Use `query` and `query.all()`

In SQLAlchemy, you use queries to interact with your database. These queries represent the selection criteria for fetching data from your tables


Unlocking New Databases with SQLAlchemy: Custom Dialect Development

SQLAlchemy provides a base class DefaultDialect you should subclass to create your dialect. This class has methods and attributes that need to be implemented or overridden to handle database-specific operations


Understanding BLOBs and SQLAlchemy: A Guide to Efficient Binary Data Storage

BLOBs are data types used in databases for storing large binary data such as images, audio files, documents, or any other kind of non-textual data


SQL, Database, SQLAlchemy: Working Together

Concepts:SQL (Structured Query Language): A language for interacting with relational databases, used for creating, reading