Checking db-models on uniqueness. INSERT OR IGNORE by means of Flask-SQLAlchemy

StackOverflow https://stackoverflow.com/questions/23309777

  •  09-07-2023
  •  | 
  •  

Вопрос

I have a question on how to add new but unique items to the database.

app = Flask(__name__, template_folder='templates')
app.config.from_object('config')
db = SQLAlchemy(app)

Classes look like this many-to-many implementation:

connections = db.Table('connections', db.metadata,
    db.Column('book_id', db.Integer, db.ForeignKey('books.id')),
    db.Column('author_id', db.Integer, db.ForeignKey('authors.id'))
)

class Author(db.Model):
    __tablename__ = 'authors'
    __searchable__ = ['a_name']
    __table_args__ = {'sqlite_autoincrement': True,}

    id = db.Column(db.Integer, primary_key=True)
    a_name = db.Column(db.String(80), unique=True)

    def __repr__(self):
        return unicode(self.a_name)


class Book(db.Model):
    __tablename__ = 'books'
    __searchable__ = ['b_name']
    __table_args__ = {'sqlite_autoincrement': True,}

    id = db.Column(db.Integer, primary_key=True)
    b_name = db.Column(db.String(80), unique=True)
    authors = db.relationship('Author', secondary=lambda: connections,
                                                    backref=db.backref('books'))

    def __repr__(self):
        return unicode(self.b_name)

I want to add only unique db items. If I will write this code:

author = Author(a_name = "Author1")
book = Book(b_name = "Book1")
author.books.append(book)
db.session.add(author)
db.session.add(book)
db.session.commit()

And we already had author with a_name "Author1" in our database there will be the error exeption.

IntegrityError: (IntegrityError) column a_name is not unique u'INSERT INTO authors (a_name) VALUES (?)' (u'\u0410\u0432\u0442\u043e\u04402',)

Do I need to check the uniqueness of this insershions and how? Or there is another best solution?

Это было полезно?

Решение

the ORM doesn't have support for the "MERGE" approach (e.g. "INSERT OR REPLACE" and all that) and there's a lot of complications with those as well. I use the unique object recipe or some variant thereof. When I'm doing a large data merge, I'll frequently load up the entire set of objects to be dealt with for some particular chunk into a dictionary ahead of time, so there's just one SELECT statement to load them, then just check into that dictionary as I proceed.

Другие советы

Actually I found not easy going solution and ugly one :), but it works. Of course, when I will use a big database I would use this unique object recipe which @zzzeek introduced.

new_author = Author(a_name = request.form['author'])
new_book = Book(b_name = request.form['book'])
author_in_db = Author.query.filter_by(a_name=unicode(new_author)).first()
book_in_db = Book.query.filter_by(b_name=unicode(new_book)).first()
# both author and book are new and unique
if unicode(new_author) != unicode(author_in_db) and \
                               unicode(new_book) != unicode(book_in_db):
    new_author.books.append(new_book)
    db.session.add(new_author)
    db.session.add(new_book)
    db.session.commit()
# just book is not unique
elif unicode(new_author) != unicode(author_in_db):
    new_author.books.append(book_in_db)
    db.session.add(new_author)
    db.session.commit()
# just author is not unique
elif unicode(new_book) != unicode(book_in_db):
    author_in_db.books.append(new_book)
    db.session.add(new_book)
    db.session.commit()
class User(Base):
  __tablename__ = 'users'

  id = Column(Integer, primary_key=True)
  name = Column(String, unique=True)
  fullname = Column(String)
  password = Column(String)

insert_command = User.__table__.insert(
    prefixes=['OR IGNORE'],
    values=dict(name='MyName', fullname='Slim Shady', password='******')
)
session.execute(insert_command)
session.commit() 

Replace works as well

prefixes=['OR REPLACE']
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top