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
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:
- When you upload a file the hidden field should be updated:
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