(Django 1.1)我有一个Project模型,该模型使用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信号无法正常工作(成员保持不变)。
我认为Peter Rowell在这种情况下的诊断是正确的。如果我从管理表单中删除“成员”字段,则post_save信号可以正常工作。包括该字段时,它将根据保存时表单中的值来保存旧成员。保存项目时,无论我对member m2m字段进行什么更改(无论是信号还是自定义保存方法),都将始终被保存之前表单中存在的成员覆盖。感谢您指出了这一点!
最佳答案
遇到相同的问题后,我的解决方案是使用m2m_changed信号。您可以在两个地方使用它,如以下示例所示。
保存后,管理员将继续执行以下操作:
保存模型字段
发出post_save信号
每平方米:
发出pre_clear
清除关系
发出post_clear
发出pre_add
再次填充
发出post_add
在这里,您有一个简单的示例,可以在实际保存数据之前更改其内容。
class MyModel(models.Model):
m2mfield = ManyToManyField(OtherModel)
@staticmethod
def met(sender, instance, action, reverse, model, pk_set, **kwargs):
if action == 'pre_add':
# here you can modify things, for instance
pk_set.intersection_update([1,2,3])
# only save relations to objects 1, 2 and 3, ignoring the others
elif action == 'post_add':
print pk_set
# should contain at most 1, 2 and 3
m2m_changed.connect(receiver=MyModel.met, sender=MyModel.m2mfield.through)
您也可以收听
pre_remove
,post_remove
,pre_clear
和post_clear
。就我而言,我使用它们来过滤另一个列表(“启用的事物”)内容中的一个列表(“活动事物”),而与列表的保存顺序无关:def clean_services(sender, instance, action, reverse, model, pk_set, **kwargs):
""" Ensures that the active services are a subset of the enabled ones.
"""
if action == 'pre_add' and sender == Account.active_services.through:
# remove from the selection the disabled ones
pk_set.intersection_update(instance.enabled_services.values_list('id', flat=True))
elif action == 'pre_clear' and sender == Account.enabled_services.through:
# clear everything
instance._cache_active_services = list(instance.active_services.values_list('id', flat=True))
instance.active_services.clear()
elif action == 'post_add' and sender == Account.enabled_services.through:
_cache_active_services = getattr(instance, '_cache_active_services', None)
if _cache_active_services:
instance.active_services.add(*list(instance.enabled_services.filter(id__in=_cache_active_services)))
delattr(instance, '_cache_active_services')
elif action == 'pre_remove' and sender == Account.enabled_services.through:
# de-default any service we are disabling
instance.active_services.remove(*list(instance.active_services.filter(id__in=pk_set)))
如果“启用”的那些已更新(清除/删除+添加回去,如在admin中一样),则“活动”的将在第一遍中缓存和清除(“ pre_clear”),然后在第二遍之后从缓存中添加回来('post_add')。
技巧是在另一个列表的m2m_changed信号上更新一个列表。
关于django - Django-如何通过post_save信号保存m2m数据?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/4432385/