在“添加”表单上将“self”选项添加到自引用外键

时间:2013-04-19 04:19:06

标签: django django-forms foreign-keys self-reference

我有一个自我引用的ForeignKey字段:

class Thing(models.Model)
    special_thing = models.ForeignKey(
        'self',
        blank=True,
        null=True
    )

在“添加事物”表格中,除了其他现有的东西,我需要提供“这件事本身”的选择,即尚未添加的事物。告诉用户添加然后重新访问该字段不是一种选择。

我应该怎么做?

我目前的想法是覆盖表格:

  • 将“special_thing”从默认的ModelChoiceField更改为ChoiceField
  • 在__init __()
  • 中添加新的特殊(“标记”)选项“*** THIS THING ***”到该字段的选项
  • 提供clean_special_thing(),允许“*** THIS THING ***”或可以从查询集中查找的Thing的内容。
  • 在save()中,如果选择“*** THIS THING ***”,请使用special_thing = None保存Thing,然后将其设置为自身并再次保存。否则按照给定的ID查找Thing并像往常一样保存。

我正在为ModelAdmin的ModelForm执行此操作。有更简单的方法吗?

2 个答案:

答案 0 :(得分:0)

另一种可能的解决方案是将jQuery(example)另一个***THIS THING***添加到模板中的表单域。

表单提交后,您可以在表单的clean - 方法或视图中检查所选选项以存储它。

例如:

if request.POST:
    if request.POST['special_thing'] == 'myself':
        # do whatever should be done
        ...

答案 1 :(得分:0)

我继续这些想法,最后一步没有必要。相反,clean方法可以将选择设置为表单的实例,整体工作仍然非常合理:

from django.contrib import admin
from django import forms

from mdoels import Thing

MARKER_THIS_THING = '*** THIS THING ***'


class ThingAdmin(admin.ModelAdmin):

    def get_form(self, request, obj=None, **kwargs):
        """
        Return the Thing ModelForm with an additional choice for the
        'special_thing' field (FK to self) that allows the Add Thing form
        to refer to the newly added instance itself.
        """

        form = super(ThingAdmin, self).get_form(request, obj, **kwargs)

        # The form still has a ModelChoiceField for special_thing, construct
        # new non-model choices from that so we can add the 'this thing' choice
        thing_choices = [c for c in form.base_fields['special_thing'].choices]

        if not obj:
            # Only the Add form needs the special choice
            thing_choices = [(MARKER_THIS_THING, MARKER_THIS_THING)] + thing_choices

        form.base_fields['special_thing'] = forms.ChoiceField(
            choices=thing_choices
        )

        def clean_special_thing(form):
            """
            Now just a simple ChoiceField, convert posted values to
            model instances like a ModelChoiceField does.
            Convert special new 'this thing' choice to be the newly added
            instance.
            """

            data = form.cleaned_data['special_thing']
            instance = getattr(form, 'instance', None)

            if data==MARKER_THIS_THING and not (instance and instance.pk):
                # Referring to new instance itself on Add form
                return instance

            # Return selected model like a ModelChoiceField does
            try:
                data = Thing.objects.get(pk=data)
            except Thing.DoesNotExist:
                raise forms.ValidationError('Invalid choice')
            return data

        # clean_* are not part of ModelAdmin, just of forms and models.
        # So we attach it to the form:
        form.clean_special_thing = clean_special_thing

        return form