I am trying to let the user upload multiple images per project. The Django Documentation enter link description here shows how to do it in generell but I think I am rendering my form differently than they are doing it. So I don´t know how to add the 'multiple' attribute in my input field. In addition they have an extra class in their views.py and inside of that the function.
views.py
def createProject(request):
form = ProjectForm()
if request.method == 'POST':
form = ProjectForm(request.POST, request.FILES)
if form.is_valid():
project = form.save(commit=False)
project.save()
context = {'form':form}
return render(request, 'projects/project_form.html', context)
models.py
class Project(models.Model):
title = models.CharField(max_length=200)
featured_images = models.ImageField(null=True, blank=True, default="default.jpg")
forms.py
class ProjectForm(ModelForm):
class Meta:
model = Project
fields = ['title', 'featured_images']
project_form.html or template file
<form class="form" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="form__field">
<label for="formInput#text">{{field.label}}</label>
{{field}}
</div>
{% endfor %}
</form>
Based on the fact that you're using a function-based view, you are wise to follow the multiple image upload tutorial from "Very Academy", although he didn't explain a few things and left you to figure out how to implement it into a real-life project. I'll show what works now inspired by this tutorial:
forms.py (a script defined in the app folder)
from *APP*.models import DashboardModel, Image
#The main form in forms.py
class RecordForm(ModelForm):
class Meta:
model = DashboardModel # (.....whatever you named it)
fields = (
#...all fields you defined in your database model
)
labels = {
#....labels including:
"img_name" : "",
"img_slug" : "",
"img_description" : "",
}
widgets = {
#....widgets including:
"img_name" : forms.TextInput(attrs={'class':'form-control', 'placeholder':'Name'}),
"img_slug" : forms.TextInput(attrs={'class':'form-control', 'placeholder':'Slug'}),
"img_description" : forms.Textarea(attrs={'class':'form-control', 'placeholder':'Description'}),
}
class ImageForm(forms.ModelForm):
class Meta:
model = Image
fields = ("image",)
labels = {"image" : "Image upload"}
widgets = {
'image': forms.ClearableFileInput(attrs={'class': 'file-upload-input', 'id': 'file-selector',"multiple": True})
}
models.py
class DashboardModel(models.Model):
#...all your main database fields, NOT including any image field
#A new model for creation of image associations:
class Image(models.Model):
project = models.ForeignKey(DashboardModel, on_delete=models.CASCADE)
image = models.ImageField()
def __str__(self):
return self.image.url # return something meaningful instead of "object"
settings.py
import os
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = "/media/"
urls.py
urlpatterns = [
...your url paths
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
views.py
def create_project(request):
if request.method == "POST":
form = ProjectForm(request.POST)
files = request.FILES.getlist("image") # get a list of images from the image association (Image model in models.py)
if form.is_valid():
f = form.save(commit=False)
f.user = request.user
f.save()
for i in files:
Image.objects.create(project=f, image=i) #Check the Image model in models.py, this infers the fields "project" and "image"
return HttpResponseRedirect(" *your database dashboard page* ")
else:
print(form.errors)
else:
form = ProjectForm()
imageform = ImageForm()
return render(request, "create_project.html", {"form": form, "imageform": imageform})
create_project.html in app/templates/app folder
You will need to define a html form (as you might have already):
<form id="post_form" method="post" action="" enctype="multipart/form-data">
Then within the form:
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{{ imageform.label }}
{{ form.img_name }} <br />
{{ form.img_slug }} <br />
{{ form.img_description }} <br />
{{ imageform.management_form }}
{% for form in imageform %}
{{ form }}
{% endfor %}
Image retrieval works by accessing the image set (getting the image URLS in this case):
_set
is the queryset which accesses the images on the database record
record
is the context name for my main model (DashboardModel)
{% for path in record.image_set.all %}
{% if not forloop.first %}
<br>
{% endif %}
<img src="{{ path }}" width='600'>
{% endfor %}
Updating images with new images (a new "update" view function in views.py) is done by adding some extra logic (you can be creative with this):
d = DashboardModel.objects.get(id=pk)
if(d.image_set.all()): #if images are present in the record
if(files): #if new files are loaded
d.image_set.all().delete() #delete the previous images
for i in files:
Image.objects.update_or_create(project=f, image=i) #create the new images
else:
pass
else: #no images already present in the record
for i in files:
Image.objects.create(project=f, image=i)