13

我有以下型号:

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

4

4 回答 4

19

文档说:

当您使用 ManyToManyField 的 through 参数指定中间模型时,管理员默认不会显示小部件。

但是,即使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
于 2012-01-05T21:22:50.120 回答
10

看看官方文档

于 2010-10-21T20:43:20.347 回答
3

我从@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为 False。但是,调用它会导致错误,因为此函数也尝试使用 through 保存多对多。您可能需要手动保存所有其他多对多关系,或捕获异常,否则。

于 2012-03-08T05:49:32.547 回答
0

throughDjango 管理员很好地支持使用参数的多对多中间模型。

例如,您有这些PersonGroup 具有中间模型的Membership模型:

models.py

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=128)

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

现在在admin.py文件中,为中间Membership 模型定义一个内联类:

@admin.register(Membership)
class MembershipInline(admin.TabularInline):
    model = Membership
    extra = 1

并在模型的管理视图中使用它们:

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    inlines = (MembershipInline,)

@admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
    inlines = (MembershipInline,)

官方文档中的更多信息:

模特管理员

于 2020-08-02T20:59:33.620 回答