【问题标题】:Django: Form with list of integersDjango:带有整数列表的表单
【发布时间】:2015-05-31 23:18:28
【问题描述】:

我有一个调用我的 django 应用程序的 javascript 应用程序(角度)。它使用整数列表来过滤响应。在 Django 中,我使用表单来清理数据。

Javascript:

app.factory('SearchData', 
      function(){
            return {
                shop:[],
                sort:'',
                xhr:'',
                brand:[],
             };
       });
app.factory('SearchQuery',
        ['$http', '$location', '$route', 'SearchData', 
        function($http, $location, $route, SearchData){
            return {
                getItems: function(){
                    return $http.get('/search/',{
                        params: SearchData,
                        responseType: 'json',
                    });
                }
            };
        }
    ]);

Python 形式:

class SearchForm(forms.Form):
    shop = forms.IntegerField(widget=forms.SelectMultiple(),required=False)
    sort = forms.CharField(max_length=1, min_length=1, required=False)
    brand = forms.IntegerField(widget=forms.SelectMultiple(),required=False)

我在商店和品牌中获得了一个整数列表,但我不知道如何在 django 端处理它。我不想使用 MultipleChoiceField,因为我需要在表单中提供选择(这会创建不必要的查询)。我想要做的就是有一个整数列表。

上面的表格抛出“输入一个整数。”。我可以放弃表单并使用 request.GET.getlist('shop') (有效)。但如果可能的话,我宁愿使用表格......

更新,现在我使用 MultipleChoiceField 并在视图中验证之前传递选择。喜欢:

shops = request.GET.getlist('shop', None)
sf = SearchForm(request.GET)
sf.fields['shop'].choices = shops

它有效,但它并不漂亮。

【问题讨论】:

    标签: javascript django forms list multiplechoicefield


    【解决方案1】:

    使用自定义小部件/字段:

    from django import forms
    from django.core.exceptions import ValidationError
    
    
    class MultipleValueWidget(forms.TextInput):
        def value_from_datadict(self, data, files, name):
            return data.getlist(name)
    
    
    class MultipleValueField(forms.Field):
        widget = MultipleValueWidget
    
    
    def clean_int(x):
        try:
            return int(x)
        except ValueError:
            raise ValidationError("Cannot convert to integer: {}".format(repr(x)))
    
    
    class MultipleIntField(MultipleValueField):
        def clean(self, value):
            return [clean_int(x) for x in value]
    
    
    class SearchForm(forms.Form):
        shop = MultipleIntField()
    

    【讨论】:

    • 答案有点冗长,您可以将 MultipleValueField 和 MultipleIntField 组合成一个对象,但效果很好!
    • 当我找到这个答案时,我高兴得跳了起来,然后发现它被 Django 1.11 破坏了,或者我可能把它推到了超出预期的范围。无论如何,我发布了我作为答案制定的(hackish)答案。 (是的,我知道我参加这个聚会迟到了,但这可能对其他人有用)
    • 还可以帮助人们知道forms.Textinput 可以替换为forms.HiddenInput (django 1.11)
    【解决方案2】:

    Udi 的代码很好,但是如果您想将其用作(例如)完全通用的用户输入表单的隐藏字段,则会出现问题(在 Django 1.11.7 下)。问题是,如果用户输入无法验证并重新发布并进行更正,则多值 POST 数据第二次返回为 repr 本身,即 ['a','b']["['a', 'b']"] 的形式返回,并随着每次重新发布而进一步受损

    因此我编写了以下函数,可用于在每次视图处理 POST 数据时修复损坏。这是一个 hack,因为它涉及使用私有变量使 request.POST 临时可变。它也不能正确处理包含逗号、转义引号等的字符串列表。

    def sanitize_keys( request, only=None):
        """ Restore multi-valued keys that have been re-posted. there's a repr
        in the round trip, somewhere. 
        only = list of keys to sanitize. Default is all of them."""
    
        mutt = request.POST._mutable
        request.POST._mutable = True
        keylist = only or request.POST.keys()
        for key in keylist:
            v = request.POST.get(key)
            if v.startswith("[") and v.endswith("]"):
                #print( "Debug: sanitizing " + v )
                sanitized=[]
                for s in v[1:-1].split(','):
                    s = s.strip()
                    if s.startswith("'") and s.endswith("'"):
                        s=s[1:-1].replace("\\'","'")
                    sanitized.append(s)
                #print( "Debug: sanitized= ", sanitized )
                request.POST.setlist( key, sanitized)
        request.POST._mutable = mutt
        return
    

    用法(片段):

    class TestForm( forms.Form):
       name = forms.CharField()
       ...
       customer_iid  = MultipleValueField( required=False) 
    
    ...
    
    # POST
    sanitize_keys( request, only=('customer_iid',) )
    #print( 'Debug: customer_iid', request.POST.getlist('customer_iid', []) )
    form = TestForm( request.POST)
    

    【讨论】:

      【解决方案3】:

      您可以使用 TypedMultipleChoiceField from Django formscoerce=int 并避免针对预定义的选项列表进行验证,覆盖 def valid_value(self, value): 方法:

      class MultipleIntegersField(forms.TypedMultipleChoiceField):
          def __init__(self, *args, **kwargs):
              super(MultipleIntegersField, self).__init__(*args, **kwargs)
              self.coerce = int
      
          def valid_value(self, value):
              return True
      
      class SearchForm(forms.Form):
          shop = MultipleIntegersField()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-07-25
        • 1970-01-01
        • 1970-01-01
        • 2017-01-04
        • 2017-07-06
        • 2011-01-18
        • 2012-02-22
        相关资源
        最近更新 更多