Изменить значение формы Django - PullRequest
1 голос
/ 23 июля 2010

Я изменил ForeignKey в форме модели, чтобы использовать вместо него TextBox.Затем я переопределяю чистый метод, чтобы вернуть объект на основе поля имени (вместо поля id)

class SongForm (forms.ModelForm):
artist = forms.CharField (widget = forms.TextInput ())

def clean_artist(self):
    data = self.cleaned_data['artist']
    artist = Artist.objects.get(name=data)
    self.cleaned_data['artist_id'] = artist.id
    return artist

class Meta:
    model = Song

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

Ответы [ 2 ]

2 голосов
/ 24 июля 2010

Я только что написал подклассы Field и Widget, которые решают эту конкретную проблему и могут использоваться, например, с автозаполнением JS - и могут использоваться повторно. Тем не менее, это потребовало больше работы, чем ваше решение, и я не уверен, захотите ли вы использовать мой или нет. В любом случае - надеюсь, я получу немного голосов - я потратил немало времени и усилий, чтобы написать это ...

Вместо того, чтобы определять вашу ModelForm, как вы делали, и возиться с clean_ Я предлагаю что-то вроде этого:

class SongForm(forms.ModelForm):
    artist = CustomModelChoiceField( queryset = Artist.objects.all(), query_field = "name" )

    class Meta:
        model = Song

Теперь CustomModelChoiceField (я не могу придумать лучшего названия для класса) - это подкласс ModelChoiceField, что хорошо, потому что мы можем использовать аргумент queryset для сужения приемлемых вариантов. Если аргумент widget отсутствует, как указано выше, используется значение по умолчанию для этого поля (подробнее об этом позже). query_field является необязательным и по умолчанию "pk". Итак, вот код поля:

class CustomModelChoiceField( forms.ModelChoiceField ):
    def __init__( self, queryset, query_field = "pk", **kwargs ):
        if "widget" not in kwargs:
            kwargs["widget"] = ModelTextInput( model_class = queryset.model, query_field = query_field )
        super( CustomModelChoiceField, self ).__init__( queryset, **kwargs )

    def to_python( self, value ):
        try:
            int(value)
        except:
            from django.core.exceptions import ValidationError
            raise ValidationError(self.error_messages['invalid_choice'])
        return super( CustomModelChoiceField, self ).to_python( value )

Что означает __init__, так это то, что установка widget = None во время создания CustomModelChoiceField дает нам простое ModelChoiceField (что было очень полезно при отладке ...). Теперь фактическая работа выполняется в виджете ModelTextInput:

class ModelTextInput( forms.TextInput ):
    def __init__( self, model_class, query_field, attrs = None  ):
        self.model_class = model_class
        self.query_field = query_field
        super( ModelTextInput, self ).__init__( attrs )

    def render(self, name, value, attrs = None ):
        try:
            obj = self.model_class.objects.get( pk = value )
            value = getattr( obj, self.query_field )
        except:
            pass
        return super(ModelTextInput, self).render( name, value, attrs )

    def value_from_datadict( self, data, files, name ):
        try:
            return self.model_class.objects.get( **{ self.query_field : data[name] } ).id
        except:
            return data[name]

По сути, это TextInput, который знает о двух дополнительных вещах - какой атрибут какой модели он представляет. (model_class следует заменить на queryset для сужения возможных вариантов на самом деле работать, я исправлю это позже). Глядя на реализацию value_from_datadict, легко определить, почему to_python в поле пришлось переопределить - он ожидает значение int, но не проверяет, является ли оно истинным, - и просто передает значение в связанную модель, которая завершается неудачно с безобразное исключение.

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

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

2 голосов
/ 24 июля 2010

Я только что получил, первоначальный хэш был то, что мне не хватало:

if self.instance.id:
    val = self.initial['artist']
    self.initial['artist'] = Artist.objects.get(id=val).name
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...