实例参数未以嵌套内联形式传递

时间:2013-08-30 01:29:05

标签: django django-forms

我正在使用我的3层模型实现嵌套内联,并且目前正在使用它。但是,即使我传递了正确的实例,我也无法限制relevantindicator下拉选项。目前relevantindicator显示下拉选择中表格中的所有值。我想将值限制为仅与disease实例关联的值。有没有办法做到这一点?

我正在使用Correct way to save nested formsets in Djangohttp://yergler.net/blog/2009/09/27/nested-formsets-with-django/作为参考。

models.py

class Disease(models.Model):
    disease = models.CharField(max_length=255)

class Indicator(models.Model):
    relevantdisease = models.ForeignKey(Disease)       
    indicator = models.CharField(max_length=255)

class IndicatorValue(models.Model):
    relevantindicator = models.ForeignKey(Indicator)
    indicator_value = models.CharField(max_length=50)

forms.py

class BaseIndicatorFormSet(BaseInlineFormSet):

     def __init__(self, *args, **kwargs):
        try:
            instance = kwargs.pop('instance')
        except KeyError:
            super(BaseIndicatorFormSet, self).__init__(*args, **kwargs)

     def save_new(self, form, commit=True):
        instance = super(BaseIndicatorFormSet, self).save_new(form, commit=commit)

        form.instance = instance

        for nested in form.nested:
            nested.instance = instance

        for cd in nested.cleaned_data:
            cd[nested.fk.name]=instance

        return instance
...

     def add_fields(self,form,index):
        super(BaseIndicatorFormSet, self).add_fields(form, index)

        try:
            instance = self.get_queryset()[index]
            pk_value = instance.pk

        except IndexError:
            instance=None
            pk_value = hash(form.prefix)

        form.nested = [
            IndicatorValueFormSet(
                disease = instance,
                queryset = IndicatorValue.objects.filter(relevantindicator = pk_value), 
                prefix = 'value_%s' % pk_value)]


class BaseIndicatorValueFormSet(BaseModelFormSet):

    def __init__(self, disease, *args, **kwargs):
        super(BaseIndicatorValueFormSet, self).__init__(*args, **kwargs)
        self.disease = disease     

    def save_new(self, form, commit=True):
        instance = form.save(commit=False)
        instance.disease = self.disease
        if commit:
           instance.save()
        return instance

    def save_existing(self, form, instance, commit=True):
        return self.save_new(form, commit)

class IndicatorValueForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        try:
            disease_obj = kwargs.pop('disease')
        except KeyError:
            super(IndicatorValueForm, self).__init__(*args, **kwargs)
            return

        super(IndicatorValueForm, self).__init__(*args, **kwargs)
        queryset = Indicator.objects.filter(relevantdisease =disease_obj)
        self.fields['relevantindicator'].queryset = queryset


disease_obj = get_object_or_404(Disease, pk=2) #hard-wired
CurriedForm = formset_factory(IndicatorValueForm, extra=3)
CurriedForm.form = staticmethod(curry(IndicatorValueForm, disease = disease_obj))
IndicatorValueFormSet = inlineformset_factory(Indicator, IndicatorValue,   formset=BaseIndicatorValueFormSet, form = CurriedForm, extra=3)
IndicatorFormSet = inlineformset_factory(Disease, Indicator, formset=BaseIndicatorFormSet, extra=0)

views.py

 disease = get_object_or_404(Disease, pk=disease_id)

 if request.method == "POST":
      formset = IndicatorFormSet(request.POST, instance=disease)

    if formset.is_valid():
       rooms = formset.save_all()
       return HttpResponseRedirect(reverse('option', kwargs={'disease_id':disease_id}))
 else:
       formset = IndicatorFormSet(instance=disease)

context = {'disease': disease, 'indicators': formset, 'hide_breadcrumb':hide_breadcrumb}
   return render_to_response('valdrui.html',context, context_instance=RequestContext(request))

template.html

  {% if relevantindicator.nested %}
  {% for formset in relevantindicator.nested %}
  {{ formset.as_table }}
  {% endfor %}
  {% endif %}

更新 我的感觉是我需要将疾病实例从form.nested传递到BaseIndicatorValueFormSet。但它似乎没有起作用。

提供清晰度的屏幕截图。

relevantindicator provides a drop-down

relatedindicator提供了一个下拉列表

When there is a indicator_value, the correct relevantindicator is selected. However, when adding a new indicator_vale, all relevantindicator for all relevantdiseases are available. I'd like to limit the relevantindicator choices to the relevantdiseases (the disease instance)

当有indicator_value时,选择正确的relatedindicator。但是,添加新的indicator_value时,所有相关疾病的所有相关指标都可用。我想将相关指标的选择限制在相关疾病(疾病实例)

更新2:我必须在instance下的BaseIndicatorFormSet中定义def __init__。我还需要在forms.py中定义表单,因为BaseIndicatorFormSet调用IndicatorValueFormSet。该实例目前是硬连线的,只是为了查看模板是否会呈现。不幸的是,模板中的表单不呈现,但也不会产生任何错误。我无法弄清楚为什么没有产生错误。

2 个答案:

答案 0 :(得分:0)

这是一个完整的forms.py,您可以将代码基于

from django import forms
from django.forms.formsets import DELETION_FIELD_NAME
from django.forms.models import BaseInlineFormSet, BaseModelFormSet, inlineformset_factory

from .models import Disease, Indicator, IndicatorValue


class BaseIndicatorFormSet(BaseInlineFormSet):

    def save_new(self, form, commit=True):
        instance = super(BaseIndicatorFormSet, self).save_new(form, commit=commit)
        form.instance = instance

        for nested in form.nested:
            nested.instance = instance

        for cd in nested.cleaned_data:
            cd[nested.fk.name]=instance

        return instance

    def add_fields(self, form, index):
        super(BaseIndicatorFormSet, self).add_fields(form, index)

        try:
            instance = self.get_queryset()[index]
            pk_value = instance.pk
        except IndexError:
            instance=None
            pk_value = hash(form.prefix)

        form.nested = [
            IndicatorValueFormSet(
                instance=instance,
                disease=instance.relevantdisease,
                prefix='value_%s' % pk_value,
                queryset=IndicatorValue.objects.filter(relevantindicator=pk_value),
            )
        ]

    def should_delete(self, form):
        """
        Convenience method for determining if the form's object will
        be deleted; cribbed from BaseModelFormSet.save_existing_objects.
        """

        if self.can_delete:
            raw_delete_value = form._raw_value(DELETION_FIELD_NAME)
            should_delete = form.fields[DELETION_FIELD_NAME].clean(raw_delete_value)
            return should_delete

        return False

    def save_all(self, commit=True):
        """
        Save all formsets and along with their nested formsets.
        """

        # Save without committing (so self.saved_forms is populated)
        # - We need self.saved_forms so we can go back and access
        #    the nested formsets
        objects = self.save(commit=False)

        # Save each instance if commit=True
        if commit:
            for o in objects:
                o.save()

        # save many to many fields if needed
        if not commit:
            self.save_m2m()

        # save the nested formsets
        for form in set(self.initial_forms + self.saved_forms):
            if self.should_delete(form): continue

            for nested in form.nested:
                nested.save(commit=commit)


class BaseIndicatorValueFormSet(BaseInlineFormSet):

    def __init__(self, disease, *args, **kwargs):
        self.disease = disease
        super(BaseIndicatorValueFormSet, self).__init__(*args, **kwargs)

    def _construct_form(self, i, **kwargs):
        form = super(BaseIndicatorValueFormSet, self)._construct_form(i, **kwargs)
        form.fields['relevantindicator'].parent_instance = self.disease
        return form

    def save_new(self, form, commit=True):
        instance = form.save(commit=False)
        instance.disease = self.disease
        if commit:
           instance.save()
        return instance

    def save_existing(self, form, instance, commit=True):
        return self.save_new(form, commit)


IndicatorValueFormSet = inlineformset_factory(
    Indicator,
    IndicatorValue,
    formset=BaseIndicatorValueFormSet,
    extra=3
)

IndicatorFormSet = inlineformset_factory(
    Disease,
    Indicator,
    formset=BaseIndicatorFormSet,
    extra=0
)

答案 1 :(得分:0)

尝试制作自定义ModelForm来过滤您的relatedindicator字段:

class IndicatorValueForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        try:
            disease_obj = kwargs.pop('disease')
        except KeyError:
            super(IndicatorValueForm, self).__init__(*args, **kwargs)
            return
        super(IndicatorValueForm, self).__init__(*args, **kwargs)
        queryset = Indicator.objects.filter(relevantdisease =disease_obj)
        self.fields['relevantindictor'].queryset = queryset

这会修改Forms init 方法以及要过滤的疾病对象,并将其添加到kwargs。

要使用它,我们需要使用curry方法将Disease对象传递到表单中:

disease_obj = <your-disease-instance>
CurriedForm = staticmethod(curry(IndicatorValueForm, disease = disease_obj))
IndicatorValueFormSet = inlineformset_factory(Indicator, IndicatorValue,   formset=BaseIndicatorValueFormSet, form = CurriedForm, extra=3)

现在,您的下拉菜单应仅显示疾病筛选的指标。