2012-01-10 5 views
14

Я создал форму Django, которая отправляется на страницу в другом домене (который я не контролирую). Идея заключается в том, что у меня есть красиво оформленная, аккуратно сгенерированная форма, которая аккуратно вписывается в мой собственный сайт и берет пользователя в другом месте, когда он отправляется.Переопределить имя поля формы Django

Однако

  • Если другая форма меняет имена любого из его полей, мне нужно изменить имена полей в моей форме, а затем изменить эти имена везде в моем приложении - так name attr связан с именем свойства, используемого для поля.
  • Если удаленная форма использует глупые имена, тогда мой объект формы также должен иметь свойства с глупыми именами, которые загрязняют код моего приложения.
  • Должны ли эти имена быть reserved words in Python (например, from), то трудно или невозможно создать представление объекта формы Django.

Есть ли способ указать другую строку, используемую для атрибута 'name', когда поле отображается? Таким образом, развязка HTML-формы из класса, который ее представляет.

Для этого потребуется два компонента:

  1. Override значение имени в виджете
  2. сделать вид чтения это значение из request.POST, когда оно связано

нужно только шаг 1 в этом случае, но шаг 2 имеет значение для решения проблем, перечисленных выше, более подробно

ответ

24

Это довольно ужасно злоупотребление API, но существует метод формы add_prefix, который вызывается для определения того, каким должно быть имя HTML каждого поля, с учетом префикса формы, если таковой имеется. Вы можете переопределить так, что он смотрит на имя поля в словаре где-то и возвращает имя, которое вы хотите - не забывая при этом сохранить существующее поведение префикса:

FIELD_NAME_MAPPING = { 
    'field1': 'html_field1', 
    'field2': 'html_field2' 
} 

class MyForm(forms.ModelForm): 
    def add_prefix(self, field_name): 
     # look up field name; return original if not found 
     field_name = FIELD_NAME_MAPPING.get(field_name, field_name) 
     return super(MyForm, self).add_prefix(field_name) 
+0

Это отличное решение моей проблемы и общая проблема привязки имени поля, привязанного к имени свойства поля объекта. Однако, если вам это нужно, у вас может возникнуть проблема. Сначала проверьте, будет ли ответ S.Lott работать на вас. Это не работает для меня, поэтому я задал этот вопрос. – adamnfish

+0

@ Даниэль: Недавно я сделал то же самое, что и вы отправил на ваш ответ. Не могли бы вы объяснить, почему вы считаете, что это ужасное злоупотребление API? Благодарю. –

+0

Честно говоря, это было почти четыре года назад, и я не могу вспомнить, почему я так думал. –

3

название на tr связан с именем свойства, используемого для поля.

Из вашего описания («Если эта другая форма изменяет имена любого из ее полей, мне нужно изменить имена полей в моей форме, а затем изменить эти имена везде в моем приложении - поскольку», «Если удаленная форма использует глупые имена, то мой объект формы должен также иметь свойства с глупыми именами, которые загрязняют код моего приложения».) Вы признаете это как хрупкое дизайнерское решение.

Вы должны учитывать, что ваша функция просмотра полностью решает эту проблему тривиально.

Вместо того, чтобы выровнять имена между удаленным приложением и вашим приложением, используйте свои функции просмотра для сопоставления ваших хороших имен с их ужасными именами.

Это то, для чего предназначены функции просмотра.

Чтобы сделать это еще дальше, ваша функция просмотра выполняет три функции.

  1. Подтвердить ввод. Возможно, они сохраняются в некоторой локальной базе данных.
  2. Данные карты из формы в форму запроса.
  3. Сделайте дистанционный запрос (через httplib или urllib2 или что угодно).

Элементы 1 и 3 не сильно меняются.

Пункт 2 представляет собой полевое сопоставление от запроса.POST к словарю, который затем вы создаете lib.urlencode для создания запроса POST. (Или независимо от протокола.)

Так что вытащите пункт 2 в гибкую вещь, которую вы указываете в своих настройках.

настройки

MY_MAPPING_FUNCTION = module.function 

В вашем views.py

def submit(request): 
    if request.method == POST: 
     form = SomeForm(request.POST) 
     if is_valid(form): 
      form.save() 
      to_be_submitted = settings.MY_MAPPING_FUNCTION(form) 
      remote_post(to_be_submitted) # or whatever your protocol is 

Добавить модуль отображения для приложения

module.py

def version_1_2(form): 
    return { 
     'silly_name_1': form.cleaned_data['your_nice_name'], 
     'from': form.cleaned_data['another_nice_name'], 
    } 

def version_2_1(form): 
    return { 
     'much_worse_name': form.cleaned_data['your_nice_name'], 
     'from': form.cleaned_data['another_nice_name'], 
    } 
+0

Спасибо, но я понимаю, что это тривиально, если я отправляю на просмотр, который я контролирую. Дело в том, что пользователь должен быть отправлен на другой сайт после отправки формы. Вы сказали «remote_post». Это нормально делать на сервере, но я не могу выдать «пост-перенаправление» клиенту, поэтому, если я не захочу повторно реализовать свою бизнес-логику (я этого не делаю), я вынужден установить действие формы на их сайт. – adamnfish

+1

«Я не могу отправить клиенту« переадресацию почты ». Это не обязательно.Вы можете отправить форму через 'urllib2', получить ответы, а затем тривиально загрузить страницу ответа другого сайта в браузер из вашего приложения. Перенаправление, похоже, произошло. Самое сложное в этом состоит в том, чтобы убедиться, что куки удаленного сайта включены в передачу в браузер пользователя. Вы создаете атаку «человек в середине». –

+0

Я уже изучил это и, к сожалению, не могу. Ответ содержит относительные URL-адреса ресурсов, поэтому мне пришлось бы написать парсер, чтобы преобразовать все пути в ответе на абсолютные URL-адреса в их домене. », а затем тривиально загрузите страницу ответов другого сайта« нет такой вещи! – adamnfish

6

Я реализовал простую функцию, которая переписывает виджет render метод и присваивает пользовательское имя:

def namedWidget(input_name, widget=forms.CharField): 
    if isinstance(widget, type): 
     widget = widget() 

    render = widget.render 

    widget.render = lambda name, value, attrs=None: \ 
     render(input_name, value, attrs) 

    return widget 

использование прост:

class AliasCreationForm(forms.Form): 
    merchant_id = forms.CharField(
     max_length=30, 
     widget=namedWidget('PSPID', forms.HiddenInput), 
    ) 
+0

Небольшая поправка к решению, виджет должен быть передан как первый аргумент для вызова функции лямбды. widget.render = lambda name, value, attrs = None: \ render (widget, input_name, value, attrs) – kumar

1

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

Создать новый виджет, который расширяет виджет вам нужно, например, TextInput:

class TextInputSansName(forms.TextInput): 
    def build_attrs(self, extra_attrs=None, **kwargs): 
     if extra_attrs: 
      extra_attrs.pop('name', None) 
     kwargs.pop('name', None) 
     return super().build_attrs(extra_attrs, **kwargs) 

Джанго называет build_attrs на виджете во время операции визуализации(). В методе я удаляю «имя», если он находится в любом из словарей. Это эффективно удаляет имя непосредственно перед его отображением.

Этот ответ переопределяет как можно меньше API Django.

На одном из моих сайтов процессор обработки платежей требует ввода данных без имен. Вот декларация контроля в классе формы:

card_number = forms.IntegerField(
    label='Card Number', 
    widget=TextInputSansName(attrs={ 'data-stripe': 'number' }) 
) 

Cheers.

+0

Это хорошо, но вопрос в том, как использовать * разные * имена (указанные) для полей, а не как чтобы полностью удалить их. Спасибо за ответ! – adamnfish

Смежные вопросы