Django - 如何通过 post_save 信号保存 m2m 数据?
(Django 1.1)我有一个项目模型,它使用 m2m 字段跟踪其成员。它看起来像这样:
class Project(models.Model):
members = models.ManyToManyField(User)
sales_rep = models.ForeignKey(User)
sales_mgr = models.ForeignKey(User)
project_mgr = models.ForeignKey(User)
... (more FK user fields) ...
创建项目时,选定的 sales_rep
、sales_mgr
、project_mgr
等 User
添加到成员中,以便更轻松地跟踪项目权限。到目前为止,这种方法非常有效。
我现在处理的问题是当User
FK字段之一通过管理员更新时如何更新项目的成员资格。我已经尝试了解决此问题的各种解决方案,但最干净的方法似乎是如下所示的 post_save
信号:
def update_members(instance, created, **kwargs):
"""
Signal to update project members
"""
if not created: #Created projects are handled differently
instance.members.clear()
members_list = []
if instance.sales_rep:
members_list.append(instance.sales_rep)
if instance.sales_mgr:
members_list.append(instance.sales_mgr)
if instance.project_mgr:
members_list.append(instance.project_mgr)
for m in members_list:
instance.members.add(m)
signals.post_save.connect(update_members, sender=Project)
但是,即使我,Project
仍然具有相同的成员通过管理员更改其中一个字段!我已经在其他项目中使用自己的视图成功更新了成员 m2m 字段,但我从来不需要让它与管理员很好地配合。
除了 post_save 信号之外,我还应该采取另一种方法来更新成员资格吗?预先感谢您的帮助!
更新:
只是澄清一下,当我在前端保存自己的表单时,post_save 信号可以正常工作(旧成员被删除,新成员被添加)。但是,当我通过管理员保存项目时,post_save 信号无法正常工作(成员保持不变)。
我认为彼得·罗威尔在这种情况下的诊断是正确的。如果我从管理表单中删除“成员”字段,则 post_save 信号可以正常工作。当包含该字段时,它会根据保存时表单中存在的值保存旧成员。无论我在保存项目时对成员 m2m 字段进行什么更改(无论是信号还是自定义保存方法),它始终会被保存之前表单中存在的成员覆盖。感谢您指出这一点!
(Django 1.1) I have a Project model that keeps track of its members using a m2m field. It looks like this:
class Project(models.Model):
members = models.ManyToManyField(User)
sales_rep = models.ForeignKey(User)
sales_mgr = models.ForeignKey(User)
project_mgr = models.ForeignKey(User)
... (more FK user fields) ...
When the project is created, the selected sales_rep
, sales_mgr
, project_mgr
, etc User
s are added to members to make it easier to keep track of project permissions. This approach has worked very well so far.
The issue I am dealing with now is how to update the project's membership when one of the User
FK fields is updated via the admin. I've tried various solutions to this problem, but the cleanest approach seemed to be a post_save
signal like the following:
def update_members(instance, created, **kwargs):
"""
Signal to update project members
"""
if not created: #Created projects are handled differently
instance.members.clear()
members_list = []
if instance.sales_rep:
members_list.append(instance.sales_rep)
if instance.sales_mgr:
members_list.append(instance.sales_mgr)
if instance.project_mgr:
members_list.append(instance.project_mgr)
for m in members_list:
instance.members.add(m)
signals.post_save.connect(update_members, sender=Project)
However, the Project
still has the same members even if I change one of the fields via the admin! I have had success updating members m2m fields using my own views in other projects, but I never had to make it play nice with the admin as well.
Is there another approach I should take other than a post_save signal to update membership? Thanks in advance for your help!
UPDATE:
Just to clarify, the post_save signal works correctly when I save my own form in the front end (old members are removed, and new ones added). However, the post_save signal does NOT work correctly when I save the project via the admin (members stay the same).
I think Peter Rowell's diagnosis is correct in this situation. If I remove the "members" field from the admin form the post_save signal works correctly. When the field is included, it saves the old members based on the values present in the form at the time of the save. No matter what changes I make to the members m2m field when project is saved (whether it be a signal or custom save method), it will always be overwritten by the members that were present in the form prior to the save. Thanks for pointing that out!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
遇到同样的问题,我的解决方案是使用 m2m_changed 信号。您可以在两个地方使用它,如以下示例所示。
保存后管理员将继续:
这里有一个简单的示例,它在实际保存数据之前更改已保存数据的内容。
您还可以收听
pre_remove
、post_remove
、pre_clear
和post_clear
。就我而言,我使用它们在另一个列表(“启用的事物”)的内容中过滤一个列表(“活动的事物”),与列表的保存顺序无关:如果“启用的”列表被更新(清除/删除) + 添加回来,就像在管理中一样),然后“活动”的在第一遍('pre_clear')中被缓存和清除,然后在第二遍('post_add')之后从缓存中添加回来。
诀窍是根据另一个列表的 m2m_changed 信号更新一个列表。
Having had the same problem, my solution is to use the m2m_changed signal. You can use it in two places, as in the following example.
The admin upon saving will proceed to:
Here you have a simple example that changes the content of the saved data before actually saving it.
You can also listen to
pre_remove
,post_remove
,pre_clear
andpost_clear
. In my case I am using them to filter one list ('active things') within the contents of another ('enabled things') independent of the order in which lists are saved:If the "enabled" ones are updated (cleared/removed + added back, like in admin) then the "active" ones are cached and cleared in the first pass ('pre_clear') and then added back from the cache after the second pass ('post_add').
The trick was to update one list on the m2m_changed signals of the other.
我看不出您的代码有任何问题,但我很困惑为什么您认为管理员的工作方式应该与任何其他应用程序不同。
但是,我必须说我认为你的模型结构是错误的。我认为您需要摆脱所有这些外键字段,而只拥有一个 ManyToMany - 但使用直通表来跟踪角色。
I can't see anything wrong with your code, but I'm confused as to why you think the admin should work any different from any other app.
However, I must say I think your model structure is wrong. I think you need to get rid of all those ForeignKey fields, and just have a ManyToMany - but use a through table to keep track of the roles.
当我需要从一组项目中查找通过 m2m_field 连接到模型的最新项目时,我陷入了困境。
根据 Saverio 的回答,以下代码解决了我的问题:
I've stuck on situation, when I needed to find latest item from set of items, that connected to model via m2m_field.
Following Saverio's answer, following code solved my issue: