82

When I'm trying to create a database schema migration, I'm getting this weird error. Can you please help me to figure out what's wrong?

$ python app.py db upgrade
[skipped]
sqlalchemy.exc.ArgumentError: Mapper Mapper|EssayStateAssociations|essay_associations could not assemble any primary key columns for mapped table 'essay_associations'

My model:

class EssayStateAssociations(db.Model):
    __tablename__ = 'essay_associations'

    application_essay_id = db.Column(
        db.Integer,
        db.ForeignKey("application_essay.id"),
        primary_key=True),
    theme_essay_id = db.Column(
        db.Integer,
        db.ForeignKey("theme_essay.id"),
        primary_key=True),
    state = db.Column(db.String, default="pending")
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Michael Samoylov
  • 2,933
  • 3
  • 25
  • 33
  • 7
    In case anyone else makes the same infuriating mistake I did, you also get this error if you use `:` instead of `=` when creating the `Column` (as you would in a pydantic model). – Dan Jan 24 '21 at 13:11

5 Answers5

94

You get this error because you have trailing commas after your Column() definitions, which cause application_essay_id and theme_essay_id to each be parsed as a one-element tuple containing a Column instead of just a Column. This stops SQLAlchemy from "seeing" that the columns are present, and consequently causes your model not to contain any primary key column.

If you simply replace

application_essay_id = db.Column(
    db.Integer,
    db.ForeignKey("application_essay.id"),
    primary_key=True),
theme_essay_id = db.Column(
    db.Integer,
    db.ForeignKey("theme_essay.id"),
    primary_key=True),

with

application_essay_id = db.Column(
    db.Integer,
    db.ForeignKey("application_essay.id"),
    primary_key=True)
theme_essay_id = db.Column(
    db.Integer,
    db.ForeignKey("theme_essay.id"),
    primary_key=True)

then your error will be fixed.

Aside: since SQLAlchemy (and Alembic and Flask-SQLAlchemy) contain some syntaxes for declaring models/tables that involve passing a comma-separated sequence of Columns as arguments (e.g. to op.create_table() or the Table() constructor) and others that involve declaring a class with Columns as class properties, it's really easy to run into this error by cutting and pasting Column declarations from the first syntax to the second and forgetting to remove some of the commas. I suspect that this easy-to-make mistake is the reason this question has such a huge number of views - over 16000 at the time that I post this answer.

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
49

You cannot have two primary keys in a table. Instead, you must use a compound primary key. This can be done by adding a PrimaryKeyConstraint in your model as below (remember to add a comma before closing the bracket in __table_args__:

from db import PrimaryKeyConstraint

class EssayStateAssociations(db.Model):
    __tablename__ = 'essay_associations'
    __table_args__ = (
        PrimaryKeyConstraint('application_essay_id', 'theme_essay_id'),
    )

    application_essay_id = db.Column(
        db.Integer,
        db.ForeignKey("application_essay.id"))
    theme_essay_id = db.Column(
        db.Integer,
        db.ForeignKey("theme_essay.id"))
    state = db.Column(db.String, default="pending")
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Minh Pham
  • 948
  • 12
  • 22
  • 18
    You can have two primary keys in a table! ref: http://docs.sqlalchemy.org/en/rel_1_0/orm/extensions/associationproxy.html#simplifying-association-objects – karantan Mar 11 '16 at 09:22
  • 6
    @karantan, no you cannot have two primary keys in a table -- that's impossible by definition. What that doco page shows is, you can have multiple *columns* in the single primary key. In some DBMSs, you can also declare additional `UNIQUE KEY` constraints that have many properties of a primary key but are by definition, not "primary". – Chris Johnson Feb 09 '18 at 13:57
  • 14
    -1 this is nonsense. It's perfectly legal to have two columns with `primary_key=True` in an SQLAlchemy model (although as @ChrisJohnson notes, this creates a compound primary key, not "two primary keys") and that was not the cause of the error here. – Mark Amery Apr 15 '18 at 14:10
  • Alas, if I do this, I do not get the proper setup, instead I get the desired two-column combined unique index, but ALSO unique constraints on each columns separately, which blows everything up. Not clear why, yet. – Joshua Eric Turcotte May 24 '19 at 13:11
  • 4
    you can declare multiple column as a primary key, the sqlalchemy, will convert it to compound key – Vivek Singh Sep 16 '19 at 07:19
  • Indeed you CAN, even MUST, have "primary_key=True" set on both columns of the association table/class. I was getting this same error because these were missing – Symen Sep 12 '20 at 21:46
21

Though good answers have been given above, One trivial mistake that one could make is to create a table without any primary key. Though it may seem unnecessary, a primary key needs to be created for every table. Else the error above gets thrown.

api_nest
  • 341
  • 2
  • 4
2

In relational database tables, it must require the candidate key. You can refer this article https://en.wikipedia.org/wiki/Candidate_key.

You just need to add primary key or composite primary key. For composite primary key, you can use below line in Flask APP. No need to import any thing. All will be take care by db variable in Flask.

In your example,

db.PrimaryKeyConstraint(application_essay_id , application_essay_id )
kepy97
  • 988
  • 10
  • 12
0

I got this error because of a syntax mistake. I.v misspell 'primary_key' in my declaration

  • I got this error due to copy-paste `primary_key=False` vs True ... :/ those little things can take you ages to discover ... :P – Ricky Levi Apr 22 '20 at 12:31