Search code examples
pythonflasksqlalchemymany-to-manyassociations

SQLAlchemy Inserting Data in a Many-to-Many Relationship with Association Table


I've seen a few questions similar to this but none quite hit the nail on the head. Essentially I have three table models Center(), Business(), and CenterBusiness() in a Flask Application using SQLAlchemy. Currently I'm adding to said relationship in this manner:

biz = Business(typId=form.type.data, name=form.name.data,
               contact=form.contact.data, phone=form.phone.data)
db.session.add(biz)
db.session.commit()

assoc = CenterBusiness(bizId=biz.id, cenId=session['center'])
db.session.add(assoc)
db.session.commit()

As you can see that's a bit ugly and I know there is a way to do it in one hit with the relationship as they are defined. I see on SQLAlchemy's docs they have a explanation of working with such a table but I can't seem to get it to work.

#Directly from SQLAlchemy Docs
p = Parent()
a = Association(extra_data="some data")
a.child = Child()
p.children.append(a)

#My Version Using my Tables
center = Center.query.get(session['center']
assoc = CenterBusiness()
assoc.business = Business(typId=form.type.data, name=form.name.data,
                          contact=form.contact.data, phone=form.phone.data)
center.businesses.append(assoc)
db.session.commit()

Unfortunately, that doesn't seem to be doing the trick... Any help would be greatly appreciated and below I've posted the models involved.

class Center(db.Model):
    id = db.Column(MEDIUMINT(8, unsigned=True), primary_key=True,
                   autoincrement=False)
    phone = db.Column(VARCHAR(10), nullable=False)
    location = db.Column(VARCHAR(255), nullable=False)
    businesses = db.relationship('CenterBusiness', lazy='dynamic')
    employees = db.relationship('CenterEmployee', lazy='dynamic')

class Business(db.Model):
    id = db.Column(MEDIUMINT(8, unsigned=True), primary_key=True,
                   autoincrement=True)
    typId = db.Column(TINYINT(2, unsigned=True),
                      db.ForeignKey('biz_type.id',
                                    onupdate='RESTRICT',
                                    ondelete='RESTRICT'),
                      nullable=False)
    type = db.relationship('BizType', backref='businesses',
                           lazy='subquery')
    name = db.Column(VARCHAR(255), nullable=False)
    contact = db.Column(VARCHAR(255), nullable=False)
    phone = db.Column(VARCHAR(10), nullable=False)
    documents = db.relationship('Document', backref='business',
                                lazy='dynamic')

class CenterBusiness(db.Model):
    cenId = db.Column(MEDIUMINT(8, unsigned=True),
                      db.ForeignKey('center.id',
                                    onupdate='RESTRICT',
                                    ondelete='RESTRICT'),
                      primary_key=True)
    bizId = db.Column(MEDIUMINT(8, unsigned=True),
                      db.ForeignKey('business.id',
                                    onupdate='RESTRICT',
                                    ondelete='RESTRICT'),
                      primary_key=True)
    info = db.relationship('Business', backref='centers',
                           lazy='joined')
    archived = db.Column(TINYINT(1, unsigned=True), nullable=False,
                         server_default='0')

Solution

  • I was able to get this working, my problem lied in the following bit of code (error in bold):

    #My Version Using my Tables
    center = Center.query.get(session['center']
    assoc = CenterBusiness()
    **assoc.info** = Business(typId=form.type.data, name=form.name.data,
                              contact=form.contact.data, phone=form.phone.data)
    center.businesses.append(assoc)
    db.session.commit()
    

    As explained in my comment in the question:

    Alright my issue was that I was not using the relationship key "info" I have in my CenterBusiness model to define the appended association. I was saying center.business thinking that the term business in that case was arbitrary. However, I needed to actually reference that relationship. As such, the appropriate key I had setup already in CenterBusiness was info.

    I will still accept any updates and/or better ways to handle this situation, though I think this is the best route at the time.