David S.
David S.

Reputation: 11168

How to define a table without primary key with SQLAlchemy?

I have a table that does not have a primary key. And I really do not want to apply this constraint to this table.

In SQLAlchemy, I defined the table class by:

class SomeTable(Base):
  __table__ = Table('SomeTable', meta, autoload=True, autoload_with=engine)

When I try to query this table, I got:

ArgumentError: Mapper Mapper|SomeTable|SomeTable could not assemble any primary key columns for mapped table 'SomeTable'.

How to loss the constraint that every table must have a primary key?

Upvotes: 60

Views: 64917

Answers (7)

Danyil Suhak
Danyil Suhak

Reputation: 61

https://docs.sqlalchemy.org/en/14/faq/ormconfiguration.html#how-do-i-map-a-table-that-has-no-primary-key

One way from there: In SQLAlchemy ORM, to map to a specific table, there must be at least one column designated as the primary key column; multi-column composite primary keys are of course also perfectly possible. These columns do not need to be known to the database as primary key columns. The columns only need to behave like a primary key, such as a non-nullable unique identifier for a row.

my code:

from ..meta import Base, Column, Integer, Date


class ActiveMinutesByDate(Base):
    __tablename__ = "user_computer_active_minutes_by_date"

    user_computer_id = Column(Integer(), nullable=False, primary_key=True)
    user_computer_date_check = Column(Date(), default=None,  primary_key=True)
    user_computer_active_minutes = Column(Integer(), nullable=True)

Upvotes: -1

Nick Holden
Nick Holden

Reputation: 3789

MSSQL Tested

I know this thread is ancient but I spent way too long getting this to work to not share it :)

from sqlalchemy import Table, event
from sqlalchemy.ext.compiler import compiles
from sqlalchemy import Column
from sqlalchemy import Integer

class RowID(Column):
    pass


@compiles(RowID)
def compile_mycolumn(element, compiler, **kw):
    return "row_number() OVER (ORDER BY (SELECT NULL))"


@event.listens_for(Table, "after_parent_attach")
def after_parent_attach(target, parent):
    if not target.primary_key:
        # if no pkey create our own one based on returned rowid
        # this is untested for writing stuff - likely wont work
        logging.info("No pkey defined for table, using rownumber %s", target)
        target.append_column(RowID('row_id', Integer, primary_key=True))

Upvotes: 6

Mikko Ohtamaa
Mikko Ohtamaa

Reputation: 83818

Here is an example using __mapper_args__ and a synthetic primary_key. Because the table is time-series oriented data, there is no need for a primary key. All rows can be unique addresses with a (timestamp, pair) tuple.

class Candle(Base):

    __tablename__ = "ohlvc_candle"

    __table_args__ = (
        sa.UniqueConstraint('pair_id', 'timestamp'),
    )

    #: Start second of the candle
    timestamp = sa.Column(sa.TIMESTAMP(timezone=True), nullable=False)

    open = sa.Column(sa.Float, nullable=False)
    close = sa.Column(sa.Float, nullable=False)
    high = sa.Column(sa.Float, nullable=False)
    low = sa.Column(sa.Float, nullable=False)
    volume = sa.Column(sa.Float, nullable=False)

    pair_id = sa.Column(sa.ForeignKey("pair.id"), nullable=False)
    pair = orm.relationship(Pair,
                        backref=orm.backref("candles",
                                        lazy="dynamic",
                                        cascade="all, delete-orphan",
                                        single_parent=True, ), )

    __mapper_args__ = {
        "primary_key": [pair_id, timestamp]
    }

Upvotes: 7

pyro hedgehog
pyro hedgehog

Reputation: 5

The solution I found is to add an auto-incrementing primary key column to the table, then use that as your primary key. The database should deal with everything else beyond that.

Upvotes: -3

medley56
medley56

Reputation: 1368

Disclaimer: Oracle only

Oracle databases secretly store something called rowid to uniquely define each record in a table, even if the table doesn't have a primary key. I solved my lack of primary key problem (which I did not cause!) by constructing my ORM object like:

class MyTable(Base)
    __tablename__ = 'stupid_poorly_designed_table'
    
    rowid = Column(String, primary_key=True)
    column_a = Column(String)
    column_b = Column(String)
    ...

You can see what rowid actually looks like (it's a hex value I believe) by running

SELECT rowid FROM stupid_poorly_designed_table
GO

Upvotes: 6

Anand Tripathi
Anand Tripathi

Reputation: 16126

There is no proper solution for this but there are workarounds for it:

Workaround 1

Adding parameter primary_key to the existing column that is not having a primary key will work.

class SomeTable(Base):
    __table__ = 'some_table'
    some_other_already_existing_column = Column(..., primary_key=True) # just add primary key to it whether or not this column is having primary key or not

Workaround 2

Just declare a new dummy column on the ORM layer, not in actual DB. Just define in SQLalchemy model

class SomeTable(Base):
    __table__ = 'some_table'
    column_not_exist_in_db = Column(Integer, primary_key=True) # just add for sake of this error, dont add in db

Upvotes: 23

mta194
mta194

Reputation: 532

There is only one way that I know of to circumvent the primary key constraint in SQL Alchemy - it's to map specific column or columns to your table as a primary keys, even if they aren't primary key themselves. http://docs.sqlalchemy.org/en/latest/faq/ormconfiguration.html#how-do-i-map-a-table-that-has-no-primary-key.

Upvotes: 43

Related Questions