5

使用 Django Grappelli 管理工具,我可以配置 ForeignKey(多对一)字段以显示为自动完成小部件,而不是下拉字段,如下所示:

class MyModel(models.Model):
    related = models.ForeignKey(RelatedModel, related_name='my_models')

class MyModelAdmin(admin.ModelAdmin):
    raw_id_fields = ('related',)
    autocomplete_lookup_fields = {
        'fk': ['related'],
    }

但是,我想做的是在另一个(一对多)方向定义自动完成小部件查找(即在RelatedModel的管理员中,以便我可以查找一个或多个 MyModel 对象)。现在,我只是在使用 ModelMultipleChoiceField:

class RelatedModelForm(forms.ModelForm):
    class Meta:
        model = RelatedModel
        fields = ('my_models',)
    my_models = forms.ModelMultipleChoiceField(queryset=MyModel.objects.all())

    def __init__(self, *args, **kwargs):
        super(SaleAdminForm, self).__init__(*args, **kwargs)
        if self.instance:
            self.fields['foods'].initial = self.instance.foods.all()

    def save(self, *args, **kwargs):
        instance = super(RelatedModelForm, self).save(commit=False)
        self.fields['my_models'].initial.update(related=None)
        self.cleaned_data['my_models'].update(related=instance)
        return instance

class RelatedModelAdmin(admin.ModelAdmin):
    model = RelatedModel
    form = RelatedModelForm

但是,有太多的 MyModel 实例无法很好地与这种类型的小部件一起使用。理想的做法是只为 MyModel 对象提供一个或多个自动完成查找小部件,以代替 ModelMultipleChoiceField。

Grappelli 有一种简单的方法可以对 FK 关系和 m2m 关系进行自动完成查找,但是有没有一种方法可以实现一对多关系?似乎这些的自动完成与其他两种类型的关系一样有用,所以我猜Grappelli也会在那里提供一种简单的方法,但我没有找到它......

4

1 回答 1

2

我最终做的是直接向 RelatedModel 类添加了一个 ForeignKey 字段,以便我可以使用它来让 Grappelli 将自动完成功能放置在页面上。该字段不用于存储任何内容,仅用于自动完成查找。因此,我的课程如下所示:

class MyModel(models.Model):
    related = models.ForeignKey(RelatedModel, related_name='my_models')

class RelatedModel(models.Model):
    # Used only for autocomplete lookups in the admin (Grappelli requires that a FK or m2m relationship exists in the model).
    mockMyModel = models.ForeignKey(MyModel, verbose_name='Add a MyModel', related_name='for_mymodel_lookups')
    # my_models - implied one-to-many field

class RelatedModelAdmin(admin.ModelAdmin):
    model = RelatedModel
    form = RelatedModelAdminForm

    raw_id_fields = ('mockMyModel',)
    autocomplete_lookup_fields = {
        'fk': ['mockMyModel'],
    }

class RelatedModelAdminForm(forms.ModelForm):
    class Meta:
        model = RelatedModel
        fields = ('mockMyModel', 'my_models',)

    # Note that you need to override __init__() and save() to get this field to work.
    my_models = forms.ModelMultipleChoiceField(queryset=MyModel.objects.all(), required=False)

    def __init__(self, *args, **kwargs):
        super(RelatedModelAdminForm, self).__init__(*args, **kwargs)
        if self.instance:
            self.fields['my_models'].initial = self.instance.my_models.all()

    def save(self, *args, **kwargs):
        # TODO: Wrap reassignments into transaction
        instance = super(RelatedModelAdminForm, self).save(commit=False)
        self.fields['my_models'].initial.update(sale=None)
        self.cleaned_data['my_models'].update(sale=instance)
        return instance

上面在页面上呈现了一个自动完成小部件(用于模拟 FK 关系)和一个多选小部件(用于一对多关系)。然后我制作了一个 jquery 插件,它从视图中隐藏了多选字段并劫持了自动完成小部件以将用户在那里选择的每个值复制到多选字段中,从而在每次选择后取消自动完成。这有点像黑客......但它完成了工作。

于 2013-07-10T17:28:44.693 回答