我正在使用我的3层模型实现嵌套内联,并且目前正在使用它。但是,即使我传递了正确的实例,我也无法限制relevantindicator
下拉选项。目前relevantindicator
显示下拉选择中表格中的所有值。我想将值限制为仅与disease
实例关联的值。有没有办法做到这一点?
我正在使用Correct way to save nested formsets in Django和http://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。但它似乎没有起作用。
提供清晰度的屏幕截图。
relatedindicator提供了一个下拉列表
当有indicator_value时,选择正确的relatedindicator。但是,添加新的indicator_value时,所有相关疾病的所有相关指标都可用。我想将相关指标的选择限制在相关疾病(疾病实例)
更新2:我必须在instance
下的BaseIndicatorFormSet中定义def __init__
。我还需要在forms.py中定义表单,因为BaseIndicatorFormSet
调用IndicatorValueFormSet
。该实例目前是硬连线的,只是为了查看模板是否会呈现。不幸的是,模板中的表单不呈现,但也不会产生任何错误。我无法弄清楚为什么没有产生错误。
答案 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)
现在,您的下拉菜单应仅显示疾病筛选的指标。