2010-07-23 2 views
1

Я изменил ForeignKey в модельной форме, вместо этого использовал TextBox. Тогда я переопределять чистый метод возвращает объект, основанный на поле имени (вместо идентификатора поле)Изменение значения формы django

класса SongForm (forms.ModelForm):
художника = forms.CharField (виджет = 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? Я думаю, перекрывая инициализации будет делать это, но не могу найти, где это значение свойства

ответ

2

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

Вместо определения вашего 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_ каждый раз, когда вы хотите использовать аналогичные функции.

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

+0

еще не пробовал, но выглядит очень интересно – juanefren

2

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

if self.instance.id: 
    val = self.initial['artist'] 
    self.initial['artist'] = Artist.objects.get(id=val).name 
Смежные вопросы