如何覆盖django modelform以实现自定义行为

时间:2013-08-26 12:44:51

标签: django django-forms

我有一个Item对象,它与另一个对象Option有很多关系。我从Item对象创建一个modelform,就像这样;

class Item(models.Model):
    category = models.ForeignKey(Category)
    name = models.CharField(max_length=200)
    price = models.DecimalField(max_digits=9, decimal_places=2, blank=True, null=True)
    options = models.ManyToManyField(Option)
class OptionForm(ModelForm):
    options = forms.ChoiceField(widget=forms.RadioSelect())
    class Meta:
        model = Item
        fields = ( 'options', )

当我在模板中呈现表单时,它会为options对象(预期行为)呈现所有可用的Item,甚至是那些不是由特定项创建的对象。我希望能够加载由用户选择的特定项目定义的options。如何覆盖表单以实现此类行为。例如,如果没有表单,我可以通过其ID呈现Items自己的Optionsitem = Item.objects.get(pk=id)

3 个答案:

答案 0 :(得分:0)

很难在运行中定义ModelForm,因为它们与模型中的结构密切相关。不过,您可以使用一些聪明的模板控制流和视图渲染来获得所需的效果。这是未经测试的,因此您可能会因此而变化。

<form method="post" action="">
    {{ formset.management_form }}
    {% for form in formset %}
        {{ form.id }}
        <ul>
     {% if user_option.category %}
            <li>{{ form.caregory }}</li>
     {% endif %}
     {% if user_option.name %}
            <li>{{ form.name }}</li>
     {% endif %}
     {% if user_option.p_opt %}
            <li>{{ form.price }}</li>
            <li>{{ form.options }}</li>
     {% endif %}
        </ul>
    {% endfor %}
</form> 

来自Djano docs here

答案 1 :(得分:0)

尝试覆盖表单的init方法并传入Item pk作为附加参数。这里的技巧是在调用父init之前弹出参数。

class ItemOptionsForm(forms.ModelForm):
    class Meta:
        model = Item

    def __init__(self, *args, **kwargs):
        # pop 'item_id' as parent's init is not expecting it
        item_id = kwargs.pop('item_id', None)

        # now it's safe to call the parent init
        super(ItemOptionsForm, self).__init__(*args, **kwargs)

        # Limit options to only the item's options
        if item_id:
            try:
                item = Item.objects.get(id=item_id)
            except:
                raise ValidationError('No item found!')

            self.fields['options'] = forms.ChoiceField(item.options)

然后,在您的视图中,创建如下表单:

form = ItemOptionsForm(item_id=item_id)

关于这一点的好处是你可以提出将在表单中显示的ValidationErrors。

请注意,这不会阻止某人向您的表单发布不属于该项的选项ID,因此您可能希望覆盖ModelForm.clean()以验证选项。

答案 2 :(得分:0)

从@jingo提供的链接django: How to limit field choices in formset?学习,我通过首先创建动态表单来解决问题;

def partial_order_item_form(item):
    """dynamic form limiting optional_items to their items"""
    class PartialOrderItemform(forms.Form):
        quantity = forms.IntegerField(widget=forms.TextInput(attrs={'size':'2', 'class':'quantity','maxlength':'5'}))
        option = forms.ModelChoiceField(queryset=OptionalItems.objects.filter(item=item),widget= forms.RadioSelect())

    return PartialOrderItemform

然后验证表格;

def show_item(request,id):
    option = get_object_or_404(Item,pk=id)
    if request.method == 'POST':
        form = partial_order_item_form(option)
        #bound form to POST data, 
        final_form = form(request.POST)
        # check validation of posted data
        if final_form.is_valid():
            order.add_to_order(request)
            url =urlresolvers.reverse('order_index',kwargs={'id':a.id})
            # redirect to order page
            return HttpResponseRedirect(url)
    else:
        form = partial_order_item_form(item=id)
    context={
        'form':form,
    }
    return render_to_response('item.html',context,context_instance=RequestContext(request))