Django管理员可以通过related_name处理一对多的关系吗?

时间:2009-11-06 23:49:39

标签: django django-models django-admin django-forms

Django管理员通过HTML< SELECT>愉快地支持多对一和多对多关系。表单字段,允许分别选择一个或多个选项。甚至还有一个很好的Javascript filter_horizontal小部件可以提供帮助。

我正试图通过related_name从一对多方面做同样的事情。我不知道它与多对多有什么不同,只要在表单中显示它,我只需要一个多选SELECT列表。但我不能简单地将related_name值添加到我的ModelAdmin派生字段列表中。

Django是否以这种方式支持一对多字段?

我的Django模型是这样的(为简化示例而设计):

class Person(models.Model):
    ...
    manager = models.ForeignKey('self', related_name='staff',
                                null=True, blank=True, )

在人员管理页面中,我可以轻松获得< SELECT>列表显示所有可能的工作人员从中选择人员的经理。我还想显示一个多选< SELECT>所有经理的员工名单。

我不想使用内联,因为我不想编辑下属的详细信息;我想要能够从列表中添加/删除人员。

(我正在尝试使用django-ajax-selects替换SELECT小部件,但这是旁边的。)

4 个答案:

答案 0 :(得分:4)

ManyToManyField的解决方案: http://code.djangoproject.com/ticket/13369#comment:2

答案 1 :(得分:1)

您可以在表单的 init 方法中设置初始值。

将它们称为self.fields ['manager_staff']。initial。

像这样:

class PersonAdminForm(forms.ModelForm):    
    manager_staff = forms.ModelMultipleChoiceField(
                        queryset=Person.objects.all(),
                        required=False,
                        )

    def __init__(self, *args, **kwargs):
        super(PersonAdminForm, self).__init__(*args, **kwargs)
        if self.instance.id is not None:
            selected_items = [ values[0] for values in Person.objects.filter(
                               #whatever your filter criteria is
                              ) ]
            self.fields['manager_staff'].initial = selected_items

答案 2 :(得分:0)

这可能最终成为其中一个限制。这就是我设法提出的。可以创建自定义表单并将其传递给管理站点以用于呈现页面。但是,我无法得到的是正确显示初始值。也许在Forms上更好的人可以使用一些我不了解的超级秘密元API并解决这个问题。

models.py

class Person(models.Model):
    # ...
    manager = models.ForeignKey('self', related_name='staff', null=True, blank=True)

    def manager_staff(self):
        return self.manager.staff.all()

admin.py

class PersonAdminForm(forms.ModelForm):    
    manager_staff = forms.ModelMultipleChoiceField(
                        initial='manager_staff',       # Can't get this to work
                        queryset=Person.objects.all(),
                        required=False,
                        )

    class Meta:
        model = Person

class PersonAdmin(admin.ModelAdmin):    
    form = PersonAdminForm

    def save_model(self, request, obj, form, change):
        for id in form.data.getlist('manager_staff'):
            # This is kind of a weak way to do this, but you get the idea...
            p = Person.objects.get(id=id)
            p.manager = obj.manager
            p.save()

        super(PersonAdmin, self).save_model(request, obj, form, change)

admin.site.register(Person, PersonAdmin)

答案 3 :(得分:0)

这是我根据T. Stone和nicoechaniz的答案提出的。初始数据的提供方式与nicoechaniz提供的方式大致相同。为了节省,您必须小心处理全新Person的情况以及编辑现有Person。这就是使save()方法复杂化的原因,具体取决于ModelForm API的非公开部分。真的,这应该建立在Django中。我想这是一个常见的特色。

class PersonAdminForm(forms.ModelForm):
    staff = forms.ModelMultipleChoiceField(
                        queryset=Person.objects.all(),
                        required=False,
                        )
    class Meta:
        model = Person

    def __init__(self, *args, **kwargs):
        super(PersonAdminForm, self).__init__(*args,**kwargs)

        if self.instance.pk is not None:
            self.initial['staff'] = [values[0] for values in self.instance.staff.values_list('pk')]

    def save(self, commit=True):
        instance = super(PersonAdminForm, self).save(commit)

        def save_m2m():
            instance.staff = self.cleaned_data['staff']

        if commit:
            save_m2m()
        elif hasattr(self, 'save_m2m'):
            save_old_m2m = self.save_m2m

            def save_both():
                save_old_m2m()
                save_m2m()

            self.save_m2m = save_both
        else:
            self.save_m2m = save_m2m

        return instance

    save.alters_data = True

class PersonAdmin(admin.ModelAdmin):
    form = PersonAdminForm