В моем приложении «задавать» может иметь несколько связанных с ним «продукты». Продукты, перечисленные в наборе, должны иметь определенное количество. Для этого отношения «многие ко многим» я следовал Документация SQLAlchemy, чтобы использовать ассоциативную таблицу с дополнительным столбцом (количество).
Я пытаюсь создать форму, в которой пользователь может назначать продукты и количества для заданного набора. И наборы, и товары уже существуют в базе данных. Данные из формы:
Это работает для создания новой ассоциации (например, набор 1 «связан» с продуктом 3 с количеством = XYZ), но я получаю ошибку целостности, когда пытаюсь обновить существующую запись.
Я могу вручную добавить отношение/запись (фиктивные данные) или в функции представления Flask следующим образом:
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()
Обновление записи (другое количество) вручную работает следующим образом:
s.products[0].quantity = 43
db.session.add(s)
db.session.commit()
Однако, когда вместо этого я использую код из первого блока (с целью обновить поле quantity
для данного существующего набора и идентификатора продукта), то есть:
a = Set_Product_Association(set=s, product=p, quantity=43)
Я получаю ошибку целостности
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)]
Я предполагаю, что это говорит мне, что я пытаюсь добавить новую запись, а не обновлять существующую.
Как мне подойти к этому? «Ручной» метод работает, но основан на определении правильного индекса в списке (т. е. для правильного product.id).
Любопытно, что если я использую form.popluate_obj(set)
в своей функции представления Flask для обработки данных формы, как описано в моем вопросе здесь, я могу обновлять поля, но не создавать новые «ассоциации». К сожалению, я не знаю, что там происходит за кулисами....
Мои модели определяются так:
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")
Редактировать: Я также попытался отменить операцию, как было предложено здесь:
s = Set.query.get(2)
a = Set_Product_Association()
a.quantity = 43
a.product = Product.query.get(3)
a.set = s
db.session.commit()
Но я все еще получаю сообщение об ошибке:
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)]
Спасибо за ваше предложение, но, как вы можете видеть в ошибке, на которую я ссылался, она имеет правильные значения, поэтому ваше предложение не имеет никакого значения.
Вы получаете ошибку целостности, потому что пытаетесь создать новый объект с теми же первичными ключами.
Этот:
a = Set_Product_Association(set=s, product=p, quantity=43)
Не обновляет, а создает.
Если вы хотите обновить актуальную строку в таблице, вам необходимо обновить существующую:
assoc = Set_Product_Association.query.filter_by(set=s, product=p).first()
assoc.quantity = 43
db.session.commit()
Кроме того, из документация рекомендуется использовать не модель, а фактическую таблицу.
Большое спасибо за вашу помощь и объяснение - очень признателен! Работает и имеет смысл. Что касается использования фактической таблицы вместо модели, для моего варианта использования здесь, где бы вы предложили записать дополнительную информацию (количество), если бы я хотел последовать этому совету?
Сохраняйте ту же структуру (набор, продукт и количество), но используйте метод db.Table
, а не расширение db.Model
. Хороший ответ, почему лучше, можно найти здесь с примерами.
Спасибо. Совершенно очевидно, что я новичок в этом, поэтому извиняюсь, если не тороплюсь. Причина, по которой я использовал модель над таблицей, заключается в том, что мне нужно хранить дополнительную информацию об отношениях. Информация, на которую вы ссылаетесь, гласит: «Короче говоря, если вам не нужно хранить дополнительные данные об отношениях, просто используйте db.Table
, это сэкономит ваше время». Итак, моя проблема в том, что я делать хочу хранить дополнительные данные...
Держите модель тогда, это не имеет значения.
a = Set_Product_Association(set_id=3, product_id=12, quantity=23)