如何在Django表单中使用带有ModelChoiceField的TextInput小部件?

时间:2016-08-28 13:38:32

标签: python django forms

在Django中,ChoiceFieldModelChoiceField代表Select小部件中的数据。当对象列表很小时,这很有用。但是,如果对象数量为数千,则管理(对于最终用户)非常困难。

为了消除上述问题,我希望最终用户在文本类型的输入框中手动输入字段值(即通过TextInput小部件)。

到目前为止,我已经创建了以下代码。由于ModelChoiceField默认情况下有Select小部件;即使将窗口小部件更改为TextInput,其行为也与之前类似。它期望模型对象的pkid值,从而引发错误:

  

选择有效的选择。这个选择不是可用的选择之一。

但是,我希望最终用户在输入框中输入sku_number字段,而不是对象的pkid。解决这个问题的正确方法是什么?

models.py

class Product(models.Model):
    sku_number = models.CharField(null=False, unique=True)
    product_name = models.CharField(null=Flase)

    def __str__(self):
        return self.sku_number

forms.py

class SkuForm(forms.Form):
    sku_number = forms.ModelChoiceField(queryset=Product.objects.all(), 
                                        widget=forms.TextInput())
    extra_field = forms.CharField(required=True)

注意:我尝试了另一种解决此问题的方法。通过切割对象的数量,仅显示最后10个对象;这样可以确保选择框中没有数千个项目。

queryset=Product.objects.all().order_by('-id')[:10]

如果正确实施,后一种方法将适用于我的特定用例,但其他人可能对前一种方法感兴趣。由于Django生成SQL语句的限制,上述声明进一步引发了错误。

  

另请注意,即使切换未评估的 QuerySet 会返回另一个未评估的 QuerySet ,也不允许进一步修改它(例如,添加更多过滤器或修改排序),因为这不能很好地转化为SQL,也不会有明确的含义。

     

来源:Django Docs

2 个答案:

答案 0 :(得分:1)

您可以使用表单clean()方法轻松完成此操作。

from django.shortcuts import get_object_or_404

class SkuForm(forms.Form):
    sku = forms.CharField(required=True)
    extra_field = forms.CharField(required=True)

    def clean(self):
        # If you're on Python 2.x, change super() to super(SkuForm, self)
        cleaned_data = super().clean()
        sku = cleaned_data['sku']
        obj = get_object_or_404(Product, sku_number=sku)
        # do sth with the Product

答案 1 :(得分:0)

就我而言,我必须将sku_number字段更改为Product对象的id。这必须在clean()之前完成。如this answer所示,__init__()应该用于在数据到达clean()之前对其进行修改。

class SkuForm(forms.Form):
    sku_number = forms.ModelChoiceField(queryset=Product.objects.all(), 
                                        widget=forms.TextInput())
    extra_field = forms.CharField(required=True)

    def __init__(self, data=None, *args, **kwargs):
        if data is not None:
            data = data.copy() # make it mutable
            if data['sku_number']:
                obj = get_object_or_404(Product, batch_name=data['sku_number'])
                data['sku_number'] = obj.id
        super(SkuForm, self).__init__(data=data, *args, **kwargs)