I have created a label_form_instance for modelchoicefield but the values in html are showing primary key values. To get rid of that, i use to_field_names but i can't provide more than one column name in it.
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',}))
You can patch the prepare_value
[GitHub] and to_python
[GitHub] functions for that, for example:
from django.core.exceptions import ValidationError
class firearmChoiceField(forms.ModelChoiceField):
def prepare_value(self, value):
if hasattr(value, '_meta'):
return '{}:{}:{}'.format(value.make,value.firearm_model,value.serial_no)
return super().prepare_value(value)
def to_python(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')
You thus should not specify the field_name
here. In fact if we look at the original implemention, we see how this field_name
is used:
class ModelChoiceField(ChoiceField): # ... def prepare_value(self, value): if hasattr(value, '_meta'): if self.to_field_name: return value.serializable_value(self.to_field_name) 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.to_field_name 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
In the prepare_value
, we thus convert an object (here a firearm_db
object) into a string that holds the value used in the <option value="...">
s. The to_python
function on the other hand performs the transformation back to a firearm
object (or None
in case the selection is empty).
You will have to ensure that the two functions are each other inverse: each mapping with prepare_value
should result in the same object when we perform a to_python
on it. If for example here the make
contains a colon (:
), then this will fail, so it might require some extra finetuning.
That being said, I am not sure why you want to use a more complicated value, and not use a primary key, a slug, or some hashed value for this.