в forms.py, когда я устанавливаю "to_field_name" в более чем одно имя столбца, это дает мне ошибку - PullRequest
1 голос
/ 11 мая 2019

Я создал label_form_instance для modelchoicefield, но значения в html показывают значения первичного ключа. Чтобы избавиться от этого, я использую to_field_names, но не могу указать в нем более одного имени столбца.

class firearmChoiceField(forms.ModelChoiceField):

    def label_from_instance(self, obj):
        return '%s%s, %s'%(obj.make,obj.firearm_model,obj.serial_no)


self.fields['firearm'] = firearmChoiceField(queryset = firearm_db.objects.all(),to_field_name="make,firearm_model,serial_no",required=False,empty_label='Select Firearm', widget = forms.Select(attrs={'label': ' ','class': 'form-control',}))

1 Ответ

0 голосов
/ 12 мая 2019

Для этого можно, например, установить патч prepare_value [GitHub] и to_python [GitHub] :

from django.core.exceptions import ValidationError

class firearmChoiceField(forms.ModelChoiceField):

    def <b>prepare_value</b>(self, value):
        if hasattr(value, '_meta'):
            return '{}:{}:{}'.format(value.make,value.firearm_model,value.serial_no)
        return super().prepare_value(value)

    def <b>to_python</b>(self, value):
        if value in self.empty_values:
            return None
        try:
            make, firmod, serial = value.split(':')
            return firearm_db.objects.get(
                make=make,
                firearm_model=firmod,
                serial_no=serial
            )
        except (ValueError, TypeError, firearm_db.DoesNotExist):
            raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice')

Таким образом, вы должны , а не указать field_name здесь. Фактически, если мы посмотрим на исходную реализацию, мы увидим, как используется field_name:

class ModelChoiceField(ChoiceField):

    # ...

    def prepare_value(self, value):
        if hasattr(value, '_meta'):
            if self.<b>to_field_name</b>:
                return value.serializable_value(self.<b>to_field_name</b>)
            else:
                return value.pk
        return super().prepare_value(value)

    def to_python(self, value):
        if value in self.empty_values:
            return None
        try:
            key = self.<b>to_field_name</b> or 'pk'
            value = self.queryset.get(**{key: value})
        except (ValueError, TypeError, self.queryset.model.DoesNotExist):
            raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice')
        return value

В prepare_value мы, таким образом, преобразуем объект (здесь firearm_db объект) в строку, содержащую значение, используемое в <option value="..."> s. С другой стороны, функция to_python выполняет преобразование обратно в объект firearm (или None, если выбор пуст).

Вы должны будете убедиться, что две функции являются друг другу обратными : каждое сопоставление с prepare_value должно приводить к одному и тому же объекту, когда мы выполняем to_python над ним. Если, например, здесь make содержит двоеточие (:), то произойдет сбой, поэтому может потребоваться дополнительная настройка.

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

...