Переопределить имя поля формы Django attr - PullRequest
15 голосов
/ 10 января 2012

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

Тем не менее,

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

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

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

  1. Переопределить значение имени в виджете
  2. Заставить форму прочитать это значение из request.POST, когда оно связано

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

Ответы [ 5 ]

29 голосов
/ 10 января 2012

Это довольно ужасное злоупотребление 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)
6 голосов
/ 23 июля 2012

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

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

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

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

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

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

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

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

Пункты 1 и 3 не сильно меняются.

Элемент 2 представляет собой сопоставление поля по полю из request.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'],
    }
1 голос
/ 05 июля 2018

Все просто.Просто используйте атрибут «метки» класса Meta:

class AuthorForm(ModelForm):
    class Meta:
        model = Author
        fields = ('name', 'title', 'birth_date')
        labels = {
            'name': 'new_name',
        }

, где «новое имя» будет именем, отображаемым в html.

1 голос
/ 04 мая 2016

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

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

Cheers.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...