Переопределить имя поля формы Django attr

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

Однако

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

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

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

  • Отмените значение имени в виджетах
  • Сделайте форму, прочитав это значение, из запроса. POST, когда он связан.

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

Ответы

Ответ 1

Это довольно ужасное злоупотребление 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)

Ответ 2

Я реализовал простую функцию, которая перезаписывает метод widget 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),
    )

Ответ 3

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

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

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

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

Это функции представления для.

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

  • Подтвердить ввод. Возможно, они сохраняются в некоторой локальной базе данных.
  • Отобразить данные из вашей формы в их структуру запроса.
  • Сделайте удаленный запрос (через httplib или urllib2 или что-то еще).

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

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

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

Настройки

MY_MAPPING_FUNCTION = module.function

В вашем view.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'],
    }

Ответ 4

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

Создайте новый виджет, который расширяет необходимый виджет, например 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)

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

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

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

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

Приветствия.