Итак, у меня есть приложение, использующее Flask -SQLAlchemy. Это база данных пустяков, и у меня есть 3 таблицы (которые актуальны здесь). trivia
для вопросов, users
для пользователей, которые могут публиковать новые вопросы, и categories
. Таким образом, у пустяков есть один столбец author_id
, указывающий на таблицу users
(извините, я так путаю, что я использую автора и пользователя) и category_id
на категорию.
Соответствующие таблицы + схемы зефира:
class Category(db.Model):
__tablename__ = 'trivia_categories'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
category = db.Column(db.String(50), unique=True)
questions = db.relationship('Trivia', backref='category', single_parent=True)
class CategorySchema(ma.ModelSchema):
class Meta:
model = Category
sqla_session = db.session
questions = fields.List(fields.Nested("TriviaSchemaCondensed", exclude=('category',)), many=True)
class Trivia(db.Model):
__tablename__ = 'trivia_questions'
id = db.Column(db.Integer, primary_key=True)
author_id = db.Column(db.Integer, db.ForeignKey('trivia_users.id'), nullable=True)
validated = db.Column(db.Boolean)
category_id = db.Column(db.Integer, db.ForeignKey('trivia_categories.id'))
difficulty = db.Column(db.String(10), nullable=False)
type = db.Column(db.String(10), nullable=False)
question = db.Column(db.String(200), nullable=False)
correct_answer = db.Column(db.String(75), nullable=False)
incorrect_answers = db.relationship(
'IncorrectAnswer',
backref='related_question',
cascade='all, delete, delete-orphan',
single_parent=True,
)
class TriviaSchema(ma.ModelSchema):
class Meta:
model = Trivia
sqla_session = db.session
category = fields.Nested("CategorySchema", exclude=('questions',), many=False)
author = fields.Pluck("UserSchema", 'nickname')
incorrect_answers = fields.List(fields.Nested("IncorrectAnswerSchema", exclude=("related_question",)))
Обратите внимание на соответствующий relationship
в Category
, указывающий на Trivia
, и что Trivia
имеет поле category_id
.
Когда вставляется новый вопрос, я добавляю объект user
к объекту trivia
. Затем я проверяю, существует ли желаемая категория (у категорий в основном только столбец id
в качестве первичного ключа и столбец category
, который имеет уникальное ограничение). Я делаю это, опрашивая БД. Если я получаю результат обратно, я просто присоединяю его к новому trivia
вопросу, в противном случае я сначала создаю и фиксирую новый category
в БД, а затем присоединяю его к вопросу.
Наконец, я совершить вопрос. Соответствующая часть контроллера:
def create(user, body):
"""
This function creates a new trivia question in the trivia structure
based on the passed in trivia data
:param trivia: trivia question to create in trivia structure
:return: 201 on success
"""
trivia = body
retrieved_user = User.query.filter_by(id=user).first()
# Create a new trivia instance using the schema and the passed in trivia
trivia_schema = TriviaSchema()
incorrect_answer_schema = IncorrectAnswerSchema()
# Save category for manipulations down the road
raw_category = trivia.get("category")
transform_incorrect_a = lambda raw_answer: { "answer": raw_answer }
trivia["incorrect_answers"] = map(transform_incorrect_a, trivia.get("incorrect_answers"))
trivia["category"] = { "category": raw_category }
trivia["validated"] = True
new_trivia = trivia_schema.load(trivia, session=db.session)
new_trivia.author = retrieved_user
# Check if category already exists
category = (
Category.query.filter(Category.category == raw_category)
.one_or_none()
)
if category is None:
category = Category(category=raw_category)
db.session.add(category)
new_trivia.category = category
# Add the trivia question to the database
db.session.add(new_trivia)
db.session.commit()
# Serialize and return the newly created trivia question in the response
data = trivia_schema.dump(new_trivia)
return data, 201
Моя проблема: сохранение всегда терпит неудачу, если категория уже существует. Я получаю IntegrityError, потому что нарушено ограничение уникальности:
sqlalchemy.exc.IntegrityError: (raised as a result of Query-invoked autoflush; consider using a session.no_autoflush block if this flush is occurring prematurely)
(MySQLdb._exceptions.IntegrityError) (1062, "Duplicate entry 'stringg' for key 'category'")
[SQL: INSERT INTO trivia_categories (category) VALUES (%s)]
Тем не менее, он не должен создавать новую запись категории в первую очередь (учитывая, что я уже извлекаю существующую запись, если это так)! Как будто SQLAlchemy всегда хочет создать новую запись категории, независимо от того, существует ли она и независимо от того, запросил ли я для этого базу данных.
Интересно, что с автором (пользователем) все в порядке - это не пытаюсь создавать новых пользователей!
Я буду очень признателен, если у кого-то есть какие-либо идеи или идеи. Я гуглил как сумасшедший и не могу найти решение.
Большое спасибо!