mbraak/django-file-form

Correct use in UpdateView

dadokkio opened this issue · 12 comments

Hello,
I was able to use the lib in a UpdateView but I want to know if there is an easier way to understand if an existing file has been removed in the update form.
My actual working code for the class:

## model.py
class Dummy(models.Model):
	user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)

class Image(models.Model):
    name = models.CharField(max_length=200)
    dummy = models.ForeignKey(Dummy, on_delete=models.CASCADE, related_name="images")
    image = models.ImageField(upload_to="images/")
## views.py
class DummytUpdateView(LoginRequiredMixin, UpdateView):
    model = Dummy
    form_class = DummytEditForm
    template_name = "update_form.html"

    def form_valid(self, form):
        instance = form.save(commit=False)
        instance.user = self.request.user
        instance.save()

		# THIS IS THE CODE I WANT TO CHECK
        uploaded_data = form.cleaned_data["files-uploads"]
        still_uploaded_pk = [x["id"] for x in ast.literal_eval(uploaded_data)]
        for image in instance.images.all():
            if image.pk not in still_uploaded_pk:
                image.delete()
                pathlib.Path(image.image.path).unlink()

        if form.cleaned_data["files"]:
            for added_file in form.cleaned_data["files"]:
                if added_file.is_placeholder:
                    continue
                Image.objects.create(
                    name=added_file.name, image=added_file.name, evidence=instance
                )
            form.delete_temporary_files()
        return super().form_valid(form)

    def get_initial(self, **kwargs):
        initial = super().get_initial(**kwargs)
        initial["files"] = [
            PlaceholderUploadedFile(x.image.path, file_id=x.pk)
            for x in self.object.images.all()
        ]
        return initial

    def get_success_url(self, **kwargs):
        return reverse("object-detail", kwargs={"pk": self.object.pk})

It's using file-uploads the right way or there is a different approach?

I'll have a look

I'm working on an example: #445 It's still a work in progress.

It's not that easy to recognise if a file has been deleted. I'm working on a solution.

I made some changes to the code that make this easier.

In the new code you don't have to use PlaceholderUploadedFile for the initial files.
NB: this only works in pr #445. It's not merged yet in master.

    def get_initial(self):
        initial = super().get_initial()

        initial["input_file"] = [example_file.input_file for example_file in self.object.files.all()]
        return initial

This is an example how to recognize deleted files.

    def form_valid(self, form):
        instance = form.save()

        # remove deleted files
        not_deleted_original_filenames = {
            f.name for f in form.cleaned_data["input_file"]
            if isinstance(f, FieldFile)
        }

        for example_file in instance.files.all():
            if example_file.input_file not in not_deleted_original_filenames:
                example_file.input_file.delete()
                example_file.delete()

        # create new files
        for f in form.cleaned_data["input_file"]:
            if not isinstance(f, FieldFile):
                try:
                    ExampleFile.objects.create(example=instance, input_file=f)
                finally:
                    f.close()

        form.delete_temporary_files()

        return HttpResponseRedirect(reverse("example_success"))

Also see FileModelFormMultipleMixin and EditModelFormMultipleView in this file.

I installed your pull and changed the configuration as indicated.
I'm just not able to recover not_deleted_original_filenames .. the value for it is an empty set so when I update old files are deleted too. The new placeholder works fine.

I tried to print also all file in form.cleaned_data['files'] and I see just new ones there.

Note that I also changed the javascript code in the pr.

Js in static folder seem updated.

The form html is pretty simple:

{% extends 'base.html' %}
{% load i18n crispy_forms_tags static %}

{% block extra_css %}
<link rel="stylesheet" href='{% static "file_form/file_form.css" %}'>
{% endblock extra_css %}

{% block extra_javascript %}
<script src='{% static "file_form/file_form.js" %}'></script>
{{ form.media }}
<script>
    initUploadFields(document.getElementById("object-form"));
</script>
{% endblock extra_javascript %}

{% block container %}
<div class="py-5 container">
    <form method="post" action="{% url 'website:object-update' pk=object.pk %}"
        id="object-form" enctype="multipart/form-data">
        <div class="card">
            <div class="card-header">
                Update <b>{{ object }}</b>
            </div>
            <div class="card-body">
                {% csrf_token %}
                {{ form|crispy }}
                {{ form.media }}
            </div>
            <div class="card-footer">
                <a href="{{ view.get_success_url }}" type="button" class="btn btn-secondary">Close</a>
                <button type="submit" class="btn btn-primary">Update</button>
            </div>
        </div>
    </form>
</div>
{% endblock %}

Something missing here?

This looks ok.

Some things to double check:

  • Does the browser load the latest js? Could be the browser cache.
  • Is there a hidden field with the name '[field]-uploads'? It should contain the original files:

Screenshot 2021-04-02 at 14 14 02

  • When you upload a file the hidden field should be updated:

Screenshot 2021-04-02 at 14 19 37

I see that you're using crispy forms. I will check if that has any influence.

I can reproduce the error. I will look into it.

I added a fix to the pr for the error.

Ok, I can confirm that with the latest fix is working properly 🔥 🔥
Thanks!!

Included in version 3.2.1