unable to create autoincrementing primary key with flask-sqlalchemy

124,909

Solution 1

Nothing is wrong with the above code. In fact, you don't even need autoincrement=True or db.Sequence('seq_reg_id', start=1, increment=1), as SQLAlchemy will automatically set the first Integer PK column that's not marked as a FK as autoincrement=True.

Here, I've put together a working setup based on yours. SQLAlechemy's ORM will take care of generating id's and populating objects with them if you use the Declarative Base based class that you've defined to create instances of your object.

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.debug = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:password@localhost/testdb'
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)

class Region(db.Model):
    __tablename__ = 'regions'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))

db.drop_all()
db.create_all()

region = Region(name='Over Yonder Thar')
app.logger.info(region.id) # currently None, before persistence

db.session.add(region)
db.session.commit()
app.logger.info(region.id) # gets assigned an id of 1 after being persisted

region2 = Region(name='Yet Another Up Yar')
db.session.add(region2)
db.session.commit()
app.logger.info(region2.id) # and 2

if __name__ == '__main__':
    app.run(port=9001)

Solution 2

So I landed here with an issue that my SQLite table wasn't auto-incrementing the primary key. I have a slightly complex use case where I want to use postgres in production but sqlite for testing to make life a bit easier when continuously deploying.

It turns out SQLite doesn't like columns defined as BigIntegers, and for incrementing to work they should be set as Integers. Remarkably SQLAlchemy can handle this scenario as follows using the with_variant function. Thought this may be useful for someone:

id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"), primary_key=True)

Further details here https://docs.sqlalchemy.org/en/13/dialects/sqlite.html

Solution 3

I think you do not need the autoincrement once you set ,

id = db.Column(db.Integer , primary_key=True , autoincrement=True)

I think that it should be ,

id = db.Column(db.Integer , primary_key=True)

it will give you the uniqueness your looking for .

Solution 4

I had this issue declaring Composite Keys on a model class.

If you are wanting an auto-incrementing id field for a composite key (ie. more than 1 db.Column(..) definition with primary_key=True, then adding autoincrement=True fixed the issue for me.

class S3Object(db.Model):
    __tablename__ = 's3_object'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)

    # composite keys
    bucket_name = db.Column(db.String(), primary_key=True)
    key = db.Column(db.String(), primary_key=True)

So the statements above about not requiring autoincrement=True should be :

you don't even need autoincrement=True, as SQLAlchemy will automatically set the first Integer PK column that's not marked as a FK as autoincrement=True unless you are defining a composite key with more than one primary_key=True

Solution 5

Your id auto increments by default even without setting the autoincrement=True flag.

So there's nothing wrong with using

id = db.Column(db.Integer, primary_key=True, autoincrement=True)

The error you're getting is as a result of attempting to populate the table with an id attribute. Your insert query shouldn't at any point contain an id attribute otherwise you'll get that error.

Share:
124,909
lovesh
Author by

lovesh

Updated on November 03, 2021

Comments

  • lovesh
    lovesh over 2 years

    I want my model's primary key to be an autoincrementing integer. Here is how my model looks like

    class Region(db.Model):
        __tablename__ = 'regions'
        id = db.Column(db.Integer, primary_key=True, autoincrement=True)
        name = db.Column(db.String(100))
        parent_id = db.Column(db.Integer, db.ForeignKey('regions.id'))
        parent = db.relationship('Region', remote_side=id, primaryjoin=('Region.parent_id==Region.id'), backref='sub-regions')
        created_at = db.Column(db.DateTime, default=db.func.now())
        deleted_at = db.Column(db.DateTime)
    

    The above code creates my table but does not make id autoincrementing. So if in my insert query I miss the id field it gives me this error

    ERROR: null value in column "id" violates not-null constraint

    So I changed the id declaration to look like this

    id = db.Column(db.Integer, db.Sequence('seq_reg_id', start=1, increment=1),
                   primary_key=True)
    

    Still the same error. What is wrong with the code above?

  • qre0ct
    qre0ct over 7 years
    I am in a similar kind of a situation. Say there are 4 records in the table as of now, from id 1 to 4. When I do a db.session.query(Table_name).delete() db.session.commit() and then if I do a db.session.add() again to add new records, the next record gets an id of 5. Since my table is now empty, I want my new records to now get an id starting from 1 again. How can I do this ?
  • lindes
    lindes over 7 years
    Sort of a side comment that I was trying to figure out a different-but-related question of how to populate my database based on model.py, and found db.drop_all() and db.create_all() to be super helpful. Thanks.
  • phyatt
    phyatt almost 6 years
    This also applies to Oracle databases, where you have to install/setup a sequence to handle the AUTOINCREMENT primary key handling.
  • Bawantha
    Bawantha over 4 years
    this is not working ` null value in column "module_id" violates not-null constraint DETAIL: Failing row contains`
  • fanfabbb
    fanfabbb about 3 years
    I'm landing here with the same issue (prod=postgresql, testing=sqlite) yet all my primary keys are already Integers. A bit absurd but this still fixed it: id = db.Column(db.Integer().with_variant(Integer, "sqlite"), primary_key=True)
  • winwin
    winwin over 2 years
    @qre0ct this is actually pointless in regard to relational databases, since you wont be able to add/delete multiple rows concurrently. Every time you change ID, your database engine will have to check if everything is all right with your indices. ID column should not be meaningful. There are some exceptions to the rule, of course. If you're worried you'll run out of IDs, just assign BigInt and forget it forever.
  • Hadi Farhadi
    Hadi Farhadi over 2 years
    this is the best answer for my problem(postgresql->production, sqlite->test). thank you