Reputation: 323
In my application, a 'set' can have a number of 'products' associated with it. Products listed against a set must have quantities defined. For this many-to-many relationship I have followed the SQLAlchemy documentation to use an association table with an additional column (quantity).
I am trying to create a form where the user can assign products and quantities against a given set. Both the sets and products already exist in the database. The data from the form are:
This works to create a new association (e.g. set 1 is 'linked' to product 3 with quantity=XYZ) but I get an integrity error when I try to update an existing record.
I can manually add a relationship/record (dummy data) or within the Flask view function as follows:
s = Set.query.get(2)
p = Product.query.get(3)
a = Set_Product_Association(set=s, product=p, quantity=23)
db.session.add(a)
db.session.commit()
Updating the record (different quantity) manually as follows works:
s.products[0].quantity = 43
db.session.add(s)
db.session.commit()
However when I use the code from the first block instead (with the aim to update the quantity
field for a given, existing set and product ID), i.e.:
a = Set_Product_Association(set=s, product=p, quantity=43)
I get an integrity error
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed: set_product_association.set_id, set_product_association.product_id [SQL: 'INSERT INTO set_product_association (set_id, product_id, quantity) VALUES (?, ?, ?)'] [parameters: (2, 3, 43)]
I assume this is to tell me that I'm trying to append a new record rather than updating the existing one.
How should I approach this? The 'manual' method works but relies on working out the correct index in the list (i.e. for the correct product.id).
Curiously, if I use form.popluate_obj(set)
in my Flask view function to process the form data as described in my question here, I can update fields but not create new 'associations'. Unfortunately, I don't know what goes on behind the scenes there....
My models are defined like so:
class Set_Product_Association(db.Model):
__tablename__ = 'set_product_association'
set_id = db.Column(db.Integer, db.ForeignKey('sets.id'), primary_key=True)
product_id = db.Column(db.Integer, db.ForeignKey('products.id'), primary_key=True)
quantity = db.Column(db.Integer)
product = db.relationship("Product", back_populates="sets")
set = db.relationship("Set", back_populates="products")
class Set(db.Model):
__tablename__ = 'sets'
id = db.Column(db.Integer, primary_key=True)
products = db.relationship("Set_Product_Association",
back_populates="set")
class Product(db.Model):
__tablename__= 'products'
id = db.Column(db.Integer, primary_key=True)
part_number = db.Column(db.String(100), unique=True, nullable=False)
sets = db.relationship("Set_Product_Association",
back_populates="product")
Edit: I've also tried reversing the operation as suggested here:
s = Set.query.get(2)
a = Set_Product_Association()
a.quantity = 43
a.product = Product.query.get(3)
a.set = s
db.session.commit()
But I still get an error:
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed: set_product_association.set_id, set_product_association.product_id [SQL: 'INSERT INTO set_product_association (set_id, product_id, quantity) VALUES (?, ?, ?)'] [parameters: (2, 3, 43)]
Upvotes: 3
Views: 4048
Reputation: 2921
You get an integrity error because you are trying to create a new object with the same primary keys.
This:
a = Set_Product_Association(set=s, product=p, quantity=43)
Does not update, but create.
If you want to update the actual row in the table, you need to update the existing one:
assoc = Set_Product_Association.query.filter_by(set=s, product=p).first()
assoc.quantity = 43
db.session.commit()
Also, from the documentation it is advised to not use a model but an actual table.
Upvotes: 7