2010-05-18 50 views
12

我有以下型号:Django管理:许多一对多列表框不以显示通过参数

class Message(models.Model): 
    date = models.DateTimeField() 
    user = models.ForeignKey(User)  
    thread = models.ForeignKey('self', blank=True, null=True) 
    ... 

class Forum(models.Model): 
    name = models.CharField(max_length=24) 
    messages = models.ManyToManyField(Message, through="Message_forum", blank=True, null=True) 
    ... 

class Message_forum(models.Model): 
    message = models.ForeignKey(Message) 
    forum = models.ForeignKey(Forum) 
    status = models.IntegerField() 
    position = models.IntegerField(blank=True, null=True) 
    tags = models.ManyToManyField(Tag, blank=True, null=True) 

在管理网站,当我去添加/更改一个论坛,我没有像你期望的那样看到消息列表框。但是,如果我在ManyToManyField声明中删除'through'参数,则会显示它。那是怎么回事?我已经在admin.py中将所有三个模型(加标签)注册到管理站点。

TIA

回答

18

文件说:

当使用通过参数传递给一个 ManyToManyField指定的中介模式,管理员将不会显示一个小部件默认。

但即使已定义through属性,也可以在管理员更改视图中显示M2M字段。

class ForumAdminForm(forms.ModelForm): 
    mm = forms.ModelMultipleChoiceField(
     queryset=models.Message.objects.all(), 
     widget=FilteredSelectMultiple(_('ss'), False, attrs={'rows':'10'})) 

    def __init__(self, *args, **kwargs): 
     if 'instance' in kwargs: 
      initial = kwargs.setdefault('initial', {}) 
      initial['mm'] = [t.service.pk for t in kwargs['instance'].message_forum_set.all()] 

     forms.ModelForm.__init__(self, *args, **kwargs) 

    def save(self, commit=True): 
     instance = forms.ModelForm.save(self, commit) 

     old_save_m2m = self.save_m2m 
     def save_m2m(): 
      old_save_m2m() 

      messages = [s for s in self.cleaned_data['ss']] 
      for mf in instance.message_forum_set.all(): 
       if mf.service not in messages: 
        mf.delete() 
       else: 
        messages.remove(mf.service) 

      for message in messages: 
       Message_forum.objects.create(message=message, forum=instance) 

     self.save_m2m = save_m2m 

     return instance 

    class Meta: 
     model = models.Forum 

class ForumAdmin(admin.ModelAdmin): 
    form = ForumAdminForm 
+0

它完美的工作,但在代码'服务'中有无效的引用。 – alex 2015-06-15 20:16:53

10
+0

这个解决方案非常简单,适合我! – 2013-06-08 11:21:44

+0

由于1.6的文档已不再可用,因此您的链接已损坏。 – 2016-06-15 19:23:21

2

我从@ Fedor的回答中了解了很多,但是一些评论和清理可能仍然有益。

class ForumAdminForm(forms.ModelForm): 
    messages = forms.ModelMultipleChoiceField(
        queryset=Message.objects.all(), 
        widget=FilteredSelectMultiple('Message', False)) 


    # Technically, you don't need to manually set initial here for ForumAdminForm 
    # However, you NEED to do the following for MessageAdminForm 
    def __init__(self, *args, **kwargs): 
     if 'instance' in kwargs: 
      # a record is being changed. building initial 
      initial = kwargs.setdefault('initial', {}) 
      initial['messages'] = [t.message.pk for t in kwargs['instance'].message_forum_set.all()] 
     super(ForumAdminForm, self).__init__(*args, **kwargs) 

    def save(self, commit=True): 
     if not self.is_valid(): 
      raise HttpResponseForbidden 
     instance = super(ForumAdminForm, self).save(self, commit) 
     def save_m2m_with_through(): 
      messages = [t for t in self.cleaned_data['messages'] 
      old_memberships = instance.message_forum_set.all() 
      for old in old_memberships: 
       if old.message not in messages: 
        # and old membership is cleaned by the user 
        old.delete() 
      for message in [x for x in messages not in map(lambda x: x.message, old_memberships)]:     
       membership = Member_forum(message=messsage, forum=instance) 
       # You may have to initialize status, position and tag for your need 
       membership.save() 
     if commit: 
      save_m2m_with_through() 
     else: 
      self.save_m2m = save_m2m_with_through 
     return instance 

    class Meta: 
     model = Forum 
     fields = {'name', 'messages') 

有一点需要注意:如果你在其他车型很多一对多关系(即不通过),super(ForumAdminForm, self).save(self, commit)将设置self.save_m2m万一commit是假。但是,调用此函数会导致错误,因为此函数也尝试使用through来保存多对多。您可能需要手动保存所有其他多对多关系,或者捕获异常,否则。

相关问题