【问题标题】:How to overide django modelform to achieve custom behaviour如何覆盖django modelform实现客户行为
【发布时间】:2013-08-28 23:04:42
【问题描述】:

我有一个 Item 对象,它与另一个对象 Option 具有多对多关系。我像这样从 Item 对象创建一个模型表单;

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)

【问题讨论】:

  • 你能澄清你的问题吗?您想动态实现用户想要的选项定义的表单吗?还是仅将用户定义的特定选项集放入模型中?
  • 我认为这个问题很好@agconti,在创建项目时,我还指定了属于该项目的options
  • 为什么要限制选项?
  • 举个例子,像咖啡这样的项目可能有大小等选项,而墨西哥卷饼等项目可能有牛排或鸡肉等选项。当我渲染表单时,它会渲染所有选项,这意味着咖啡会有大、小、牛排和鸡肉等选项,这是不对的。
  • 看看这里:stackoverflow.com/questions/9743103/…。也许这会导致正确的方向。

标签: django django-forms


【解决方案1】:

从@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))

【讨论】:

    【解决方案2】:

    尝试覆盖表单的 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() 以验证选项。

    【讨论】:

      【解决方案3】:

      很难动态定义 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 文档 here

      【讨论】:

        猜你喜欢
        • 2016-03-20
        • 2011-07-12
        • 2012-04-10
        • 1970-01-01
        • 2015-02-14
        • 2012-01-31
        • 2018-02-01
        • 2015-03-15
        • 1970-01-01
        相关资源
        最近更新 更多