0

I have a foreign key inside a model and I want to customize the way it appears in my form. By default a select appears with option values as id. I want to change the option values to be another field.

Models.py:

class PriceDetails(models.Model):
    id = models.AutoField(primary_key=True)
    size = models.ForeignKey(SizeOd, blank=True, null=True, on_delete=models.DO_NOTHING)

    def __str__(self):
        return str(self.id)
class SizeOd(models.Model):
    size = models.DecimalField(unique=True, decimal_places=1, max_digits=5)
    multiplier = models.DecimalField(unique=True, decimal_places=3, max_digits=8)

    def __str__(self):
        return str(self.size)

Forms.py:

class PriceDetailsForm(forms.ModelForm):
    size = forms.ModelChoiceField(queryset=SizeOd.objects.order_by('size'), to_field_name="multiplier", required=False)
    class Meta:
        model = PriceDetails
        exclude = ('id',)

Views.py:

class PriceDetailsInline(InlineFormSetFactory):
    model = models.PriceDetails
    form = forms.PriceDetailsForm
    fields = '__all__'
    factory_kwargs = {'extra': 1, 'can_delete': False}

class CreateInvoiceView(LoginRequiredMixin, CreateWithInlinesView):
    model = models.Invoice
    form = forms.InvoiceForm
    inlines = [PriceDetailsInline]
    fields = '__all__'
    template_name = 'app/invoice_form.html'

Template:invoice_form.html:

<form method="post">
    {% csrf_token %}
    {{form.as_p}}
    <div id="FormSet" class="my_inline">
        {% for formset in inlines %}
            <table id="FormSet{{ formset.prefix }}{{ forloop.counter }}">
                {{ formset.as_table }}
            </table>
            {{ formset.management_form }}

        {% endfor %}
    </div>
    <input type="submit" value="Create Invoice" >
</form>

<script type="text/javascript">
$(function() {
    {% for formset in inlines %}
        $('table#FormSet{{ formset.prefix }}{{ forloop.counter }}').formset();
    {% endfor %}
});
</script>

Default:

<select>
<option value='id1'>size1</option>
<option value='id2'>size2</option>
</select>

Expected:

<select>
<option value='*multiplier1*'>size1</option>
<option value='multiplier2'>size2</option>
</select>

EDIT: My output The option values here are ids, I want them to be multiplers. ModelChoiceField does not make any changes. The select box (size field) appears the same if I eliminate 'size = forms.ModelChoiceField(queryset=SizeOd.objects.order_by('size'), to_field_name="multiplier", required=False)' altogether. Instead, it should sort the display text on select box according to size and append multipliers as values.

2 Answers2

0

You can customize the model's appearance in forms by changing the return field

class PriceDetails(models.Model):
    ...

    def __str__(self):
        return str(self.size) # <--change this here from .id --> .size

Edited

EchoCloud
  • 61
  • 5
0

Found the solution here. The problem was not with the ModelChoiceField. None of the changes made in forms.py was reflected in the forms. (because of the use of InlineFormSetFactory) Overwrote construct_inlines() (which is part of extra_views.advanced.ModelFormWithInlinesMixin) in views.py

class CreateInvoiceView(LoginRequiredMixin, CreateWithInlinesView):
model = models.Invoice
form = forms.InvoiceForm
inlines = [PriceDetailsInline]
fields = '__all__'
template_name = 'app/invoice_form.html'
def construct_inlines(self):
    inline_formsets = super(CreateInvoiceView, self).construct_inlines()
    inline_formsets[0].forms[0].fields['size'].queryset = models.SizeOd.objects.order_by('size')
    inline_formsets[0].forms[0].fields['size'].to_field_name = 'multiplier'
    return inline_formsets