Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove a relation many-to-many (association object) on Sqlalchemy

I'm stuck with a SqlAlchemy problem.

I just want to delete an relation. This relation is made by an association object.

models

class User(db.Model, UserMixin):
    id                  = db.Column(db.Integer, primary_key=True)
    email               = db.Column(db.String(255), unique=True)
    username            = db.Column(db.String(255), unique=True)
    password            = db.Column(db.String(255))
    following           = db.relationship('Follower', foreign_keys='Follower.user_id')
    followed_by         = db.relationship('Follower', foreign_keys='Follower.follow_user_id')

    def __repr__(self):
        return '<%s (%i)>' % (self.username, self.id)

class Follower(db.Model):
    __tablename__       = 'followers'

    user_id             = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
    follow_user_id      = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
    created_at          = db.Column(db.DateTime, default=datetime.datetime.now)

    user_followed       = db.relationship("User", primaryjoin=(follow_user_id==User.id))
    user                = db.relationship("User", primaryjoin=(user_id==User.id))

    def __repr__(self):
        return '<%i %i>' % (self.user_id, self.follow_user_id)

How I add a relation (it works !):

u1 = # user 1
u2 = # user 2

...

f = Follower()
f.user_followed = u2
u1.following.append(f)
db.session.commit()

How I try do delete a relation (it doesn't work):

f = Follower()
f.user_followed = u2

u1.following.remove(f)
db.session.commit()

The error

ValueError: list.remove(x): x not in list

I understand why it doesn't work, it's because this Follower() instance is not in the list u1.following. So, how can I delete this relation?

like image 625
Dragu Avatar asked Sep 30 '22 11:09

Dragu


2 Answers

You can override __eq__, __ne__, and __hash__ so that instances that are not the same instance, but have the same values, compare and hash equal.

I use the following mixin for this purpose. Just override compare_value in the subclass to return whatever should actually be compared.

from sqlalchemy import inspect

class EqMixin(object):
    def compare_value(self):
        """Return a value or tuple of values to use for comparisons.
        Return instance's primary key by default, which requires that it is persistent in the database.
        Override this in subclasses to get other behavior.
        """
        return inspect(self).identity

    def __eq__(self, other):
        if not isinstance(other, self.__class__):
            return NotImplemented

        return self.compare_value() == other.compare_value()

    def __ne__(self, other):
        eq = self.__eq__(other)

        if eq is NotImplemented:
            return eq

        return not eq

    def __hash__(self):
        return hash(self.__class__) ^ hash(self.compare_value())
like image 119
davidism Avatar answered Oct 04 '22 22:10

davidism


One could also try querying for the object first and then delete it from the list.

follower_to_be_deleted = db.session.query(Follower).filter_by(user_id=u2.id).first()
u1.following.remove(follower_to_be_deleted)
db.session.commit()
like image 42
apurva.nandan Avatar answered Oct 04 '22 22:10

apurva.nandan