question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Imagefield layout not working + missing name when there are multiple image inline formsets

See original GitHub issue
  • Package version: 1.11.2
  • Django version: 3.2
  • Python version: 3.9
  • Template pack: Bootstrap4

Description:

I noticed that:

  1. When there is multiple image inline formsets, the filenames do not appear after upload, but the files there because it will appear in database table once the form is submitted
  2. ImageField’s ClearableFileInput do not follow the layout defined

image

If there is only single image inline formset, filename appear just fine.

image

In this image, you see 2 image upload, both with ---, but I have already uploaded 2 images, thus problem 1.

If you refer to below forms.py, you see that I have defined the layout, is_primary field should be before image field, but that is not the html generated, thus problem 2.

# forms.py
class PersonImageForm(forms.ModelForm):
    class Meta:
        model = PersonImage
        fields = "__all__"
        widgets = {
            "image": forms.ClearableFileInput(
                attrs={"multiple": False},
            ),
        }

        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.helper = FormHelper()
            self.helper.form_tag = False
            self.helper.disable_csrf = True
            self.helper.layout = Layout(
                Row(
                    Column(
                        "is_primary", css_class="form-group col-md-2 mb-0",
                    ),
                    Column(
                        "image", css_class="form-group col-md-10 mb-0",
                    ),
                ),
            )


PersonImageFormSet = forms.models.inlineformset_factory(
    Person, PersonImage, form=PersonImageForm, can_delete=False, extra=0, min_num=1, validate_min=True,
)

template

{% extends "base.html" %}
{% load static %}
{% load crispy_forms_tags %}

{% block content %}

<div class="container-fluid">
  <h1>Create Person</h1>
  <form enctype="multipart/form-data" method="post">
    {% csrf_token %}
    {% crispy form %}

    {% if images %}
    <h3>Create Image(s)</h3>
    {{ images.management_form|crispy }}
    <div id="person_image_formset">
      {% for form in images.forms %}
      <table class="no_error">
        {% crispy form %}
      </table>
      {% endfor %}
    </div>
    <input class="btn btn-outline-secondary" id="person_image_add_more" type="button" value="Add more image">
    <div id="person_image_empty_form" style="display:none">
      <table class="no_error">
        {% crispy images.empty_form %}
      </table>
    </div>
    {% endif %}
  </form>
</div>
{% endblock %}

{% block scriptblock %}
{{ block.super }}
document.querySelector("#person_image_add_more").addEventListener("click", function() {
  var form_idx = document.querySelector("#id_person_image-TOTAL_FORMS").value
  document.querySelector("#person_image_formset").innerHTML += document.querySelector("#person_image_empty_form").innerHTML.replace(/__prefix__/g, form_idx)
  document.querySelector("#id_person_image-TOTAL_FORMS").value = parseInt(form_idx) + 1
});
{% endblock %}

html output

<div id="person_image_formset">
  <div id="div_id_person_image-0-image" class="form-group">
    <label for="id_person_image-0-image" class="requiredField">
      Image<span class="asteriskField">*</span>
    </label>
    <div class="mb-2">
      <div class="form-control custom-file" style="border: 0">
        <input
          type="file"
          name="person_image-0-image"
          class="custom-file-input"
          accept="image/*"
          id="id_person_image-0-image"
        />
        <label
          class="custom-file-label text-truncate"
          for="id_person_image-0-image"
          >---</label
        >
        <script type="text/javascript" id="script-id_person_image-0-image">
          document
            .getElementById("script-id_person_image-0-image")
            .parentNode.querySelector(".custom-file-input").onchange =
            function (e) {
              var filenames = "";
              for (let i = 0; i < e.target.files.length; i++) {
                filenames += (i > 0 ? ", " : "") + e.target.files[i].name;
              }
              e.target.parentNode.querySelector(
                ".custom-file-label"
              ).textContent = filenames;
            };
        </script>
      </div>
    </div>
  </div>
  <div class="form-group">
    <div
      id="div_id_person_image-0-is_primary"
      class="custom-control custom-checkbox"
    >
      <input
        type="checkbox"
        name="person_image-0-is_primary"
        class="checkboxinput custom-control-input"
        id="id_person_image-0-is_primary"
      />
      <label for="id_person_image-0-is_primary" class="custom-control-label">
        Is primary
      </label>
      <small id="hint_id_person_image-0-is_primary" class="form-text text-muted"
        >Tick if this is primary image</small
      >
    </div>
  </div>
  <table class="no_error">
    <input
      type="hidden"
      name="csrfmiddlewaretoken"
      value="ofoZMUaNboqwVHl0UonJ2ihl5xSimkfSN3MrIiFaH3HDv2M7ab9TyglLZymu2b5S"
    />
    <input
      type="hidden"
      name="person_image-0-person"
      id="id_person_image-0-person"
    />
    <input type="hidden" name="person_image-0-id" id="id_person_image-0-id" />
  </table>

  <div id="div_id_person_image-1-image" class="form-group">
    <label for="id_person_image-1-image" class="requiredField">
      Image<span class="asteriskField">*</span>
    </label>
    <div class="mb-2">
      <div class="form-control custom-file" style="border: 0">
        <input
          type="file"
          name="person_image-1-image"
          class="custom-file-input"
          accept="image/*"
          id="id_person_image-1-image"
        />
        <label
          class="custom-file-label text-truncate"
          for="id_person_image-1-image"
          >---</label
        >
        <script type="text/javascript" id="script-id_person_image-1-image">
          document
            .getElementById("script-id_person_image-1-image")
            .parentNode.querySelector(".custom-file-input").onchange =
            function (e) {
              var filenames = "";
              for (let i = 0; i < e.target.files.length; i++) {
                filenames += (i > 0 ? ", " : "") + e.target.files[i].name;
              }
              e.target.parentNode.querySelector(
                ".custom-file-label"
              ).textContent = filenames;
            };
        </script>
      </div>
    </div>
  </div>
  <div class="form-group">
    <div
      id="div_id_person_image-1-is_primary"
      class="custom-control custom-checkbox"
    >
      <input
        type="checkbox"
        name="person_image-1-is_primary"
        class="checkboxinput custom-control-input"
        id="id_person_image-1-is_primary"
      />
      <label for="id_person_image-1-is_primary" class="custom-control-label">
        Is primary
      </label>
      <small id="hint_id_person_image-1-is_primary" class="form-text text-muted"
        >Tick if this is primary image</small
      >
    </div>
  </div>
  <table class="no_error">
    <input
      type="hidden"
      name="csrfmiddlewaretoken"
      value="ofoZMUaNboqwVHl0UonJ2ihl5xSimkfSN3MrIiFaH3HDv2M7ab9TyglLZymu2b5S"
    />
    <input
      type="hidden"
      name="person_image-1-person"
      id="id_person_image-1-person"
    />
    <input type="hidden" name="person_image-1-id" id="id_person_image-1-id" />
  </table>
</div>

Help is greatly appreciated 🙏

Preferably also include:

  • Example Django Crispy Forms code
  • Screenshots
  • Actual HTML generated
  • Expected HTML

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
shawnngtqcommented, May 17, 2021

Gotcha, anyway, I tried using bootstrap 5, CreateView / UpdateView for image formsets are working as expected. The only thing for improvement is just to use bootstrap 5 input css

image

I am going to close this issue as I believe there is no point fixing outdated software.

For others’ future reference. The working code for dcf bootstrap 5 is here https://github.com/shawnngtq/django_dcf_image_example

Thanks @smithdc1

0reactions
smithdc1commented, May 17, 2021

One of the aims we’ve had for a long time is to split the template packs from the core app. So yes you’ll need to install the bootstrap5 pack and update the setting as you noted.

With bootstrap 4 we had to ship some JS in the crispy-form template to get it to work. Clearly some bug in that code where formsets are in play.

For Bootstrap 5 they changed how these inputs work and so no custom JS. Therefore this may just work is you try the bootstrap5 template pack. 🤔

Read more comments on GitHub >

github_iconTop Results From Across the Web

Imagefield layout not working + missing name when there are ...
If there is only single image inline formset, filename appear just fine. image. In this image, you see 2 image upload, both with...
Read more >
Django inline-formset with an image field not updating
The issues I am having are: 1) If there is a photo attached to the listing, through the admin, the photo form does...
Read more >
Django crispy form imagefield example - GitHub
Problem 1. When there is multiple image inline formsets, the filenames do not appear after upload, but the files there because it will...
Read more >
Problem with ImageField and inline admin form - Google Groups
Hi. I'm stumped and can't figure out a solution for the following: With the models: class Entry(models.Model): pub_date = models.
Read more >
Module and theme fields - HubSpot Developers
Add fields to modules and themes to allow content creators to control various aspects of a page within the page editor.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found