In my app, users can upload files. These files are represented by a model with attributes for associated meta-data (upload time, file name, user created note, max value, etc).
A user will upload several files and I want to show them a table of their uploaded files with a checkbox next to each row that can be used to delete selected files and associated model instances.
I'm not sure what the right approach is, I've looked at the following options but there doesn't seem to be an obvious solution:
model forms, using CheckboxSelectMultiple
django_tables2 - seems to be an established 3rd party app
reuse the django admin code (form and view).
The default django admin app behavior is perfect for my use case, but I'm not sure what the best way is to reproduce it?
app/models.py
import uuid
from django.contrib.auth import get_user_model
from django.db import models
User = get_user_model()
class Document(models.Model):
def rename_file(self, filename):
ext = filename.split('.')[-1]
new_name = uuid.uuid4().hex
return f'documents/{new_name}.{ext}'
owner = models.ForeignKey(
User,
on_delete=models.CASCADE,
editable=True,
)
document = models.FileField(upload_to=rename_file)
notes = models.CharField(max_length=258, blank=True)
uploaded_at = models.DateTimeField(auto_now_add=True)
nice_name = models.CharField(max_length=128, null=True, blank=False)
start_date = models.DateField(null=True, blank=False)
end_date = models.DateField(null=True, blank=False)
def __str__(self):
return str(self.document)
app/admin.py
from django.contrib import admin
from .models import Document
def uuid(obj):
return obj.owner.uuid
uuid.short_description = 'user id'
@admin.register(Document)
class DocumentAdmin(admin.ModelAdmin):
fields = ('document', 'notes')
list_display = (
'owner',
uuid,
'document',
'nice_name',
'notes',
'uploaded_at',
'start_date',
'end_date'
)
app/forms.py
from django import forms
from django.forms.widgets import CheckboxSelectMultiple
from .models import Document
class DeleteDocumentsForm(forms.ModelForm):
document = forms.ModelMultipleChoiceField(queryset=None, widget=forms.CheckboxSelectMultiple)
nice_name = forms.ModelMultipleChoiceField(queryset=None, widget=forms.CheckboxSelectMultiple)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
qs = Document.objects.filter(owner_id=user)
# super(DeleteDocumentsForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.fields['document'].queryset = qs
self.fields['nice_name'].queryset = qs.values('nice_name')
# document.fields['nice_name'].widget.attrs['readonly'] = True
class Meta:
model = Document
fields = ('document', 'nice_name')
widgets = {
'document': CheckboxSelectMultiple,
'nice_name': CheckboxSelectMultiple,
}
To achieve what you want to do, you need to create a form that encapsulates a table. When the for loop iterates over each item from your database, provide a checkbox which value would be the unique ID of the uploaded file. By submitting the form, you would also then be submitting these IDs.
So this would be your view:
<form action="." method="post">
{% csrf_token %}
<table>
{% for upload in uploads %}
<tr>
<td><input type="checkbox" name="selected" value="{{ upload.id }}"></td>
<td><img src="{{ upload.url }}" alt="image"></td>
<td>{{ upload.name }}</td>
</tr>
{% endfor %}
</table>
<button type="submit">Delete</button>
</form>
Once the user has selected one or multiple checkboxes, and after pressing on delete, you will receive a query dict with the IDs of the selected items that you'll be able to process as you want in your view:
<QueryDict: {'csrfmiddlewaretoken': [''], 'selected': ['2', '3']}>
As for the form, I don't really see any use of it IMO.