Сохранение модели формы с использованием MultiWidget и MultiValueField - PullRequest
4 голосов
/ 16 сентября 2011

Я пытаюсь понять, как создавать формы, создав подклассы MultiWidgets и MultiValueFields.У меня есть простая модель адреса и связанные формы:

class Address(models.Model):
    user = models.ForeignKey(User)
    city = models.CharField(max_length=255)
    state = models.CharField(choices = settings.STATES, max_length=50)
    postal = models.CharField(max_length=10)
    address = models.TextField()

    class Meta:
        verbose_name_plural = 'Addresses'

class AddressFieldWidget(forms.MultiWidget):
    def decompress(self,value):
        if value:
            return [value[0],value[1],value[2]]
        return ''

    def format_output(self, rendered_widgets):
        str = ''
        line_1 = '<td class="align_left"><label for="contact_phone">Address Line 1</label></td>'

        for field in rendered_widgets:
            str += '<tr>' + line_1
            str += '<td class="align_right">%s</td></tr>' % field
        return '<tr>' + str + '</tr>'

    def value_from_datadict(self,data,files,name):
        line_list = [widget.value_from_datadict(data,files,name+'_%s' %i) for i,widget in enumerate(self.widgets)]
        try:
            return line_list[0] + ' ' + line_list[1] + ' ' + line_list[2]       
        except:
            return ''

class AddressField(forms.MultiValueField):
    def __init__(self,*args,**kwargs):
        fields = (
            forms.CharField(widget=forms.TextInput(attrs={'class':'big'})),
            forms.CharField(widget=forms.TextInput(attrs={'class':'big'})),
            forms.CharField(widget=forms.TextInput(attrs={'class':'big'})),
        )
        super(AddressField,self).__init__(*args,**kwargs)
        self.widget = AddressFieldWidget(widgets=[fields[0].widget, fields[1].widget, fields[2].widget])

    def compress(self, data_list):
        return data_list[0] + ' ' + data_list[1] + ' ' + data_list[2]


class AddressFormNew(forms.ModelForm):
    postal = forms.CharField(widget=forms.TextInput(attrs={'class':'small'}))
    address = AddressField()
    city = forms.CharField(widget=forms.TextInput(attrs={'class':'big'}))

    class Meta:
        model = Address

Ну, я не могу понять, как использовать эту форму на мой взгляд.Я пытаюсь сделать:

@login_required
def render_addresses(request):
    address_form = AddressFormNew()
    if request.method == 'POST':
        address_form = AddressFormNew(request.POST)
        if address_form.is_valid():
            address_form.save()
            return HttpResponse('ok')
        else:
            return HttpResponse(address_form.errors['address'])

    return render_to_response('profile/addresses.html',context_instance=RequestContext(request,{'address_form':address_form}))

В результате Django выдает мне эту ошибку:

Введите список значений.

Кроме того, когда я пытаюсь напечатать request.POST.items (), он дает адрес ответа как 3 отдельных данных.

Я совершенно заблудился здесь, я должен получить данные об адресе в одну строку.Как мне добиться этого, только сохранив мою форму?

Я действительно ценю это, если кто-то даст мне четкое объяснение.

Ответы [ 3 ]

4 голосов
/ 21 сентября 2011

Вот проблемы, которые я вижу в вашем коде, которые должны решить эту проблему:

(1).В вашем методе AddressField init при вызове суперкласса init вы должны передать в качестве аргумента fields .

class AddressField(forms.MultiValueField):
    def __init__(self,*args,**kwargs):
        fields = (
            forms.CharField(widget=forms.TextInput(attrs={'class':'big'})),
            forms.CharField(widget=forms.TextInput(attrs={'class':'big'})),
            forms.CharField(widget=forms.TextInput(attrs={'class':'big'})),
        )
        self.widget = AddressFieldWidget(widgets=[fields[0].widget, fields[1].widget, fields[2].widget])
        super(AddressField,self).__init__(fields=fields,*args,**kwargs)

(2).Вы правы, ваш value_from_datadict неверен.Дело в том, что вы использовали MultiValueField для заполнения виджетом.Таким образом, виджет должен возвращать список значений в соответствующие подполя в AddressField

. Вы можете просто вызвать value_from_datadict суперкласса, который выполнит эту работу, или использовать это(что я думаю, то же самое):

def value_from_datadict(self,data,files,name):
     res = []
     for i, widget in enumerate(self.widgets):
         res.append(widget.value_from_datadict(data, files, name + '_%s' % i))
     return res

Важно понимать основную концепцию.Вы могли бы использовать этот виджет с CharField тоже.В этом случае value_from_datadict должен был вернуть строку.Но так как вы используете MultiValueField, тип возвращаемого значения должен быть списком.Это и есть причина, по которой «введите список значений» как ошибка

Просто дополнительная мысль: вам не следует использовать пробел в качестве разделителя, если вы планируете воссоздать адресную строку1, 2 и 3 иззначения хранятся в базе данных в форме.Если нет, то все хорошо:)

Я не нашел хороших примеров для MultiValueField и MultiWidget в документации или в сети, но так как мне пришлось использовать их в одном из моих проектов, мне пришлось копаться в немсебя.Надеюсь, это поможет:)

0 голосов
/ 19 сентября 2011

Исходные файлы Django часто могут быть хорошим источником вдохновения. (Пун не предназначен) Вы можете, например, проверить django.forms.fields.SplitDateTimeField, который дает пример того, как сделать что-то подобное.

Некоторые возможные ошибки могут быть связаны с тем, что вы устанавливаете self.widget после инициализации (super(AddressField,self).__init__()), поэтому в поле используется только стандартный виджет. И вы не отправили fields с __init__. Вот краткий набросок того, как, я думаю, вы могли бы сделать AddressField:

class AddressField(forms.MultiValueField):
    widget = MultiValueWidget(widgets=(forms.TextInput, forms.TextInput, forms.TextInput)
    def __init__(self, *args, **kwargs):
        fields = (
            forms.CharField(widget=forms.TextInput(attrs={'class':'big'})),
            forms.CharField(widget=forms.TextInput(attrs={'class':'big'})),
            forms.CharField(widget=forms.TextInput(attrs={'class':'big'})),
        )
        super(AddressField,self).__init__(fields, *args, **kwargs)

    def compress(self, data_list):
        return "%s %s %s" % data_list[0:2]

Это исключает все помехи, которые у вас были в AddressFieldWidget, потому что я, честно говоря, не совсем понял, что вы пытались сделать:)

0 голосов
/ 16 сентября 2011

Я думаю, вам также нужен метод распаковки, и поля = (...) должны быть за пределами __init__

...