[Answer]-Django inlineformset_factory with predefined modelchoice field rendered as text

0👍

Well, it seems like a hack, but the only solution I’ve found so far is to create a dummy form field to store a label value. Below is the code I have so far.

I’m not super happy with this and I’d love to see any other solutions.

forms.py:

class ProcessRequestForm(ModelForm):
    class Meta:
        model = ProcessRequest
        exclude = ('process', 'request_user')


class ProcessRequestInputValueForm(ModelForm):
    value_label = forms.CharField(widget=forms.HiddenInput())
    process_input = forms.ModelChoiceField(
        queryset=ProcessInput.objects.all(),
        widget=forms.HiddenInput())

    class Meta:
        model = ProcessRequestInputValue
        exclude = ('process_request',)

views.py:

def create_process_request(request, process_id):
    process = get_object_or_404(Process, pk=process_id)
    process_request = ProcessRequest(process=process, request_user=request.user)

    PRInputValueFormSet = inlineformset_factory(
        ProcessRequest,
        ProcessRequestInputValue,
        form=ProcessRequestInputValueForm,
        extra=process.processinput_set.count(),
        can_delete=False,
    )

    if request.method == 'POST':
        # validate the form / formset and save
    else:
        form = ProcessRequestForm(instance=process_request)
        initial_data = list()

        for process_input in process.processinput_set.all():
            # note 'value_label' is used for the 'value' field's label
            initial_data.append(
                {
                    'process_input': process_input,
                    'value_label': process_input.input_name
                })

        formset = PRInputValueFormSet(instance=process_request, initial=initial_data)

    return render_to_response(
        'create_process_request.html',
        {
            'process': process,
            'form': form,
            'formset': formset,
        },
        context_instance=RequestContext(request)
    )

template:

Process: {{ process.process_name }}<br/>

<form action="{% url 'create_process_request' process.id %}" method="post">
  {% csrf_token %}

  {{ form.non_field_errors }}
  {{ formset.management_form }}
  <Inputs: <br/>

    {% for input_form in formset %}
      {% for hidden_field in input_form.hidden_fields %}

        {% if hidden_field.name == 'process_input' %}
          {{ hidden_field.as_hidden }}
        {% elif hidden_field.name == 'value_label' %}

          {# Needed if form submission has errors, else our custom label goes away #}
          {# Could be used to "hack" our custom label on POST %}
          {{ hidden_field.as_hidden }}

          {# Our custom dynamic label #}
          {{ hidden_field.value }}

        {% endif %}
      {% endfor %}

      {{ input_form.value }}

      {% if input_form.value.errors %}
        {{ input_form.value.errors|striptags }}
      {% endif %}

      <hr/>

    {% endfor %}
      <button type="submit">Save</button>
    </div>
  </div>
</form>

And a much more usable form:

enter image description here

👤Fiver

1👍

The way I did it for my inline_formsets was to simply exclude the field you don’t wish to display in the form, here the process_input as well as the process_request

class ProcessRequestInputValueForm(ModelForm):
    class Meta:
        model = ProcessRequestInputValue
        exclude = ('process_request','process_input')

Now set a property value on the underlying model (ProcessRequestInputValue) to answer what you do want, in your case the Process object on the ProcessInput I’m guessing:

@property
def my_process(self):
    return self.process_input.process

then in your template, just call that unary method directly, from inside the form loop:

<span>Process Input: {{form.instance.my_process}}</span>

which will call the unicode method on the Process object

This avoids having to make any complicated ReadOnlyWidgets etc.

0👍

Did you tried disabling?

some_field = forms.ChoiceField(choices=my_choices, 
                               widget=forms.Select(attrs={'disabled':'disabled'}))

Leave a comment