Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deleting from a SQLAlchemy many-to-many matches the wrong number of rows [duplicate]

I try to delete many-to-many object in Flask using SQLAlchemy. I have these models, which are similar to the reference:

tags = db.Table('tags',
    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id')),
    db.Column('document_id', db.Integer, db.ForeignKey('document.id'))
)
class Tag(db.Model):
    """The Tag class"""
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(256), unique=True)

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return '<Tag %r>' % self.name


class Document(db.Model):
    """The Document class"""
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(256))
    abstract = db.Column(db.String(256))
    body = db.Column(db.Text)

    # A Document has one syntax
    syntax_id = db.Column(db.Integer, db.ForeignKey('syntax.id'))
    syntax = db.relationship('Syntax', backref=db.backref('documents', lazy='dynamic'))

    is_private = db.Column(db.Boolean)
    completion = db.Column(db.Integer) #1, 2, 3 of 3
    creation_date = db.Column(db.DateTime)
    mod_date = db.Column(db.DateTime)
    license = db.Column(db.String(256))    
    # A Document has one Author
    author_id = db.Column(db.Integer, db.ForeignKey('author.id'))
    author = db.relationship('Author', backref=db.backref('documents', lazy='dynamic'))

    # A Document has many tags
    tags = db.relationship('Tag', secondary=tags, backref=db.backref('documents', lazy='dynamic'))
    # A Document has one Category
    category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
    category = db.relationship('Category', backref=db.backref('documents', lazy='dynamic'))

    def __init__(self, title, body, is_private=None, creation_date=None):
        self.title = title
        self.body= body
        if is_private is None:
            self.is_private=False
        else:
            self.is_private = is_private
        self.completion = 1
        self.license = "CC-BY 4.0"
        if creation_date is None:
            self.creation_date = datetime.utcnow()
        else:
            self.creation_date = creation_date
        self.mod_date = datetime.utcnow()

    def __repr__(self):
        return '<Document %r>' % self.title

The important here is that Document class has many-to-many relationship with Tag class. In the shell I remove one tag and everything is ok

>>> from aplicacio import db
>>>from aplicacio import Tag, Document
>>>d = Document.query.get(2)
>>>d.tags
[<Tag 'proportionality - linear function'>, <Tag 'how'>, <Tag 'when'>]
>>>d.tags.remove(t)
>>>d.tags
[<Tag 'proportionality - linear function'>, <Tag 'when'>]

But this fails when I run commit:

db.session.commit()
2017-01-30 20:59:48,706 INFO sqlalchemy.engine.base.Engine DELETE FROM tags WHERE tags.tag_id = ? AND tags.document_id = ?
2017-01-30 20:59:48,706 INFO sqlalchemy.engine.base.Engine (2, 2)
2017-01-30 20:59:48,707 INFO sqlalchemy.engine.base.Engine ROLLBACK
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/site-packages/sqlalchemy/orm/scoping.py", line 157, in do
    return getattr(self.registry(), name)(*args, **kwargs)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 874, in commit
    self.transaction.commit()
  File "/usr/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 461, in commit
    self._prepare_impl()
  File "/usr/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 441, in _prepare_impl
    self.session.flush()
  File "/usr/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 2139, in flush
    self._flush(objects)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 2259, in _flush
    transaction.rollback(_capture_exception=True)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/util/langhelpers.py", line 60, in __exit__
    compat.reraise(exc_type, exc_value, exc_tb)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/util/compat.py", line 187, in reraise
    raise value
  File "/usr/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 2223, in _flush
    flush_context.execute()
  File "/usr/lib/python3.6/site-packages/sqlalchemy/orm/unitofwork.py", line 389, in execute
    rec.execute(self)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/orm/unitofwork.py", line 503, in execute
    self.dependency_processor.process_saves(uow, states)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/orm/dependency.py", line 1092, in process_saves
    secondary_update, secondary_delete)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/orm/dependency.py", line 1113, in _run_crud
    result.rowcount)
sqlalchemy.orm.exc.StaleDataError: DELETE statement on table 'tags' expected to delete 1 row(s); Only 5 were matched.
like image 995
somenxavier Avatar asked Jan 22 '26 05:01

somenxavier


1 Answers

It seems that we have many association post-tag as explained here and there. Solved with

tags = db.Table('tags',
    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id')),
    db.Column('document_id', db.Integer, db.ForeignKey('document.id')),
    db.PrimaryKeyConstraint('tag_id', 'document_id')
)
like image 79
somenxavier Avatar answered Jan 23 '26 19:01

somenxavier



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!