Джанго: Форма со списком целых чисел
У меня есть приложение javascript (на английском языке), которое вызывает мое приложение django. Он использует списки целых чисел для фильтрации ответа. В 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
Это работает, но это не красиво.
2 ответа
Используйте пользовательский виджет / поле:
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()
Вы можете использовать TypedMultipleChoiceField
из Django
forms
с участием coerce=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()
Код Уди хорош, но есть проблема (в Django 1.11.7), если вы хотите использовать это как (скажем) скрытое поле полностью общей формы пользовательского ввода. Проблема заключается в том, что если пользовательский ввод не проходит проверку и повторно помещается с исправлениями, многозначные данные POST возвращаются во второй раз как repr
само по себе, т.е. ['a','b']
возвращается как ["['a', 'b']"]
и далее калечат с каждым повторным постом
Поэтому я написал следующую функцию, которую можно использовать для исправления повреждения каждый раз, когда представление обрабатывает данные POST. Это взлом, потому что это включает в себя создание 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)