将两个或多个现有子项附加到父级会导致IntegrityError,Query-invoked autoflush

时间:2016-05-09 12:50:43

标签: python sql sqlalchemy

使用documentation中的示例,我正在尝试将现有子项附加到新父项。首先,我尝试了这个例子:

>>> p = models.Parent()
>>> a1 = models.Association(extra_data='data1')
>>> a2 = models.Association(extra_data='data2')
>>> a1.child = models.Child()
>>> a2.child = models.Child()
>>> p.children.append(a1)
>>> p.children.append(a2)
>>> db.session.add(p)
>>> db.session.commit() # OK!

现在我的DB中有两个孩子和一个父母。然后我试了一下:

>>> p2 = models.Parent()
>>> a1 = models.Association(extra_data='data3')
>>> a2 = models.Association(extra_data='data4')
>>> a1.child = models.Child.query.get(1)
>>> a2.child = models.Child.query.get(2) # Oops!

Traceback (most recent call last):
  File "<console>", line 1, in <module>
    ✂-----< ... >-------
  File "/home/boss/codes/python_projects/c/flaskenv/lib/python3.5/site-packages/MySQLdb/connections.py", line 280, in query
 _mysql.connection.query(self, query)
sqlalchemy.exc.IntegrityError: (raised as a result of Query-invoked
 autoflush; consider using a session.no_autoflush block if this flush
 is occurring prematurely) (_mysql_exceptions.IntegrityError) (1364,
 "Field 'left_id' doesn't have a default value") [SQL: 'INSERT INTO 
association (right_id, extra_data) VALUES (%s, %s)'] [parameters: (1,
 'data3')]

# Let's continue 
>>> db.session.rollback()
>>> a1.child
<app.models.Child object at 0x7f7bac39ffd0>
>>> a2.child

>>> a2.child = models.Child.query.get(2)
>>> a2.child
<app.models.Child object at 0x7f7bac2465c0>
>>> p2.children.append(a1)
>>> p2.children.append(a2)
>>> db.session.add(p2)
>>> db.session.commit() # OK!
>>> 

我做错了什么? 注意:我没有使用自动提交,我已经管理了将子项附加到父项,并在session.rollback()之后将它们添加到数据库中。

错误解释指出我“考虑使用session.no_autoflush块”,但是我需要更多地澄清这种机制是如何工作的,如果我禁用autoflush,它可能会导致另一个错误或安全威胁。

1 个答案:

答案 0 :(得分:0)

好像我找到了一些解决方法:
使用session.no_autoflush(作为错误解释提供):

>>> p2 = models.Parent()
>>> a1 = models.Association(extra_data='data5')
>>> a2 = models.Association(extra_data='data6')
>>> with db.session.no_autoflush:
...     a1.child = models.Child.query.get(1)
...     a2.child = models.Child.query.get(2)
... 
>>> p2.children.append(a1)
>>> p2.children.append(a2)
>>> db.session.add(p2)
>>> db.session.commit()
>>> 

但是如果你不想厌倦冲洗东西,只需按照以下方式逐个追加孩子:

>>> p2 = models.Parent()
>>> a1 = models.Association(extra_data='data9')
>>> a2 = models.Association(extra_data='data10')
>>> a1.child = models.Child.query.get(1)
>>> p2.children.append(a1)
>>> a2.child = models.Child.query.get(2)
>>> p2.children.append(a2)
>>> db.session.add(p2)
>>> db.session.commit()

无论如何,我认为自动清除机制及其引起的错误仍需要更多解释。