保存后未立即更新多对多关系的问题
我遇到了未更新的多对多关系问题 当我保存模型(通过管理员)并尝试在模型中使用新值时 附加到 post_save
信号或在 save_model
内的函数 关联的AdminModel
。 我尝试使用以下方法在这些函数中重新加载对象 get 函数具有 id.. 但它仍然具有旧值。
这是交易问题吗?当 交易结束?
谢谢,
I'm having issues with ManytoMany Relationships that are not updating
in a model when I save it (via the admin) and try to use the new value within a
function attached to the post_save
signal or within the save_model
of
the associated AdminModel
.
I've tried to reload the object within those functions by using the
get function with the id.. but it still has the old values.
Is this a transaction issue? Is there a signal thrown when the
transaction ends?
Thanks,
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
当您通过管理表单保存模型时,它不是原子事务。首先保存主对象(以确保它具有 PK),然后清除 M2M,并将新值设置为表单中出现的任何内容。因此,如果您处于主对象的 save() 中,那么您就处于 M2M 尚未更新的机会窗口中。事实上,如果您尝试对 M2M 执行某些操作,则更改将被clear() 消除。大约一年前我遇到了这个问题。
代码与 ORM 重构之前相比有所变化,但归结为 django.db.models.fields.ManyRelatedObjectsDescriptor 和 ReverseManyRelatedObjectsDescriptor 中的代码。看看他们的 __set__() 方法,你会看到manager.clear(); manager.add(*value)clear() 完成会清除该表中当前主对象的任何 M2M 引用。然后 add() 设置新值。
所以回答你的问题:是的,这是一个交易问题。
交易结束时是否抛出信号?没有任何官方内容,但请继续阅读:
几个月前有一个相关线程 MonkeyPatching 是提出的一种方法。 Grégoire 为此发布了 MonkeyPatch。我还没有尝试过,但看起来应该有效。
When you save a model via admin forms it's not an atomic transaction. The main object gets saved first (to make sure it has a PK), then the M2M is cleared and the new values set to whatever came out of the form. So if you are in the save() of the main object you are in a window of opportunity where the M2M hasn't been updated yet. In fact, if you try to do something to the M2M, the change will get wiped out by the clear(). I ran into this about a year ago.
The code has changed somewhat from the pre-ORM refactor days, but it boils down to code in
django.db.models.fields.ManyRelatedObjectsDescriptor
andReverseManyRelatedObjectsDescriptor
. Look at their __set__() methods and you'll seemanager.clear(); manager.add(*value)
That clear() complete cleans out any M2M references for the current main object in that table. The add() then sets the new values.So to answer your question: yes, this is a transaction issue.
Is there a signal thrown when the transaction ends? Nothing official, but read on:
There was a related thread a few months ago and MonkeyPatching was one method proposed. Grégoire posted a MonkeyPatch for this. I haven't tried it, but it looks like it should work.
当您尝试访问模型的 post_save 信号中的 ManyToMany 字段时,相关对象已被删除,并且直到信号完成后才会再次添加。
要访问此数据,您必须绑定到 ModelAdmin 中的 save_lated 方法。不幸的是,您还必须在 post_save 信号中包含需要自定义的非管理请求的代码。
请参阅: https://docs .djangoproject.com/en/1.7/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_lated
示例:
然后在信号中您可以进行与保存时要执行的相同更改:
When you are trying to access the ManyToMany fields in the post_save signal of the model, the related objects have already been removed and will not be added again until after the signal is finished.
To access this data you have to tie into the save_related method in your ModelAdmin. Unfortunately you'll also have to include the code in the post_save signal for non-admin requests that require customization.
see: https://docs.djangoproject.com/en/1.7/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_related
Example:
Then in your signals you can make the same changes that you want to execute on a save:
我对此有一个通用的解决方案,它看起来比猴子修补核心甚至使用芹菜更干净(尽管我确信有人可以找到它失败的区域)。基本上,我在管理中为具有 m2m 关系的表单添加一个 clean() 方法,并将实例关系设置为 clean_data 版本。这使得实例的保存方法可以使用正确的数据,即使它还没有“记录在案”。尝试一下,看看效果如何:
I have a general solution to this that seems a bit cleaner than monkey-patching the core or even using celery (although I'm sure someone could find areas where it fails). Basically I add a clean() method in the admin for the form that has the m2m relationships, and set the instance relations to the cleaned_data version. This make the correct data available to the instance's save method, even though it's not "on the books" yet. Try it and see how it goes:
请参阅 http://gterzian。 github.io/Django-Cookbook/signals/2013/09/07/manipulated-m2m-with-signals.html
问题:
当您在 post 或 pre_save 信号接收器中操作模型的 m2m 时,您的更改将在 Django 的后续“清除”m2m 中被清除。
解决方案:
在 post 或 pre_save 信号处理程序中,将另一个处理程序注册到要更新其 m2m 的模型的 m2m 中间模型上的 m2m_changed 信号。
请注意,第二个处理程序将接收多个 m2m_changed 信号,测试与它们一起传递的“action”参数的值是关键。
在第二个处理程序中,检查“post_clear”操作。当您收到带有 post_clear 操作的信号时,m2m 已被 Django 清除,您有机会成功操作它。
示例:
请参阅 https://docs.djangoproject.com/en/ 1.5/ref/signals/#m2m-changed
See http://gterzian.github.io/Django-Cookbook/signals/2013/09/07/manipulating-m2m-with-signals.html
problem:
When you manipulate the m2m of a model within a post or pre_save signal receiver, your changes get wiped out in the subsequent 'clearing' of the m2m by Django.
solution:
In you post or pre_save signal handler, register another handler to the m2m_changed signal on the m2m intermediary model of the model whose m2m you want to update.
Please note that this second handler will receive several m2m_changed signals, and it is key to test for the value of the 'action' arguments passed along with them.
Within this second handler, check for the 'post_clear' action. When you receive a signal with the post_clear action, the m2m has been cleared by Django and you have a chance to successfully manipulate it.
an example:
see https://docs.djangoproject.com/en/1.5/ref/signals/#m2m-changed
如果您使用
on_commit
将会有所帮助,因此多对多关系将被更新。It would help if you use
on_commit
, so ManyToMany Relationships will be updated.您可以在此线程中找到更多信息:Django Manytomany Signals?
You can find more informations in this thread : Django manytomany signals?
更新 m2m 以及更新您的模型之一的解决方案之一。
Django 1.11 及更高版本
首先,通过管理面板的所有请求都是原子的。你可以看看模型管理:
您在更新过程中可以观察到的行为,即当您对 m2m 记录所做的更改未保存时,即使您在模型之一或信号中的保存方法中进行了更改,也只会发生这种情况,因为 m2m 形式会重写主记录之后的所有记录对象已更新。
这就是为什么,一步一步:
主对象已更新。
您的代码(在保存方法或信号中)进行了更改(您可以查看它们,只需在 ModelAdmin 中放置一个断点):
不幸的是,这个解决方案的缺点是 - 您对 m2m 的更改将在单独的事务中执行。
One of the solutions to update m2m, along with updating one of your models.
Django 1.11 and higher
First of all, all requests via admin panel are atomic. You can look at ModelAdmin:
The behavior which you can observe during updating, when changes which you made with m2m records were not saved, even after you made them in a save method one of your models or in a signal, happens only because m2m form rewrites all records after the main object is updated.
This is why, step by step:
The main object is updated.
Your code(in a save method or in a signal) made changes (you can look at them, just put a breakpoint in ModelAdmin):
Unfortunately, the downside of this solution - your changes with m2m will be executed in a separate transaction.