Reputation: 47
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>
Upvotes: 0
Views: 2573
Reputation: 395
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)
Upvotes: 0
Reputation: 359
There are multiple ways to associate multiple images to a single field in Django. One way I like to do is:
class MyModel(models.Model):
field = models.CharField(max_lenght=255)
class MyModelImage(models.Model):
image = models.ImageField(upload_to="/where_you_want_to")
field = models.ForeignKey(MyModel,related_name="images",on_delete=models.CASECADE)
I hope this helps you out
Edit:
# this is how you model might look
class ParentModel(models.Model):
# your model
pass
class ImageModel(models.Model):
image = models.ImageField(upload_to="your_path/")
parent = models.ForeignKey(ParentModel,on_delete=models.CASECADE,related_name="images")
# this is for view
# create your custom inlineforset form
# with first argument is your model with which you want to associate multiple images and second is your image model
CustomInlineForm = inlineformset_factory(ParentModel, ImageModel)
# use this custom inline form like this
formset = CustomInlineForm(request.FILES or None, instance=your_instance)
# optionally you can also get all your image using related name also like this:
parent_instance.images
Upvotes: 2
Reputation: 2623
First you have to slightly adjust your form to allow for multiple upload:
# forms.py
class ProjectForm(ModelForm):
class Meta:
featured_images = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
model = Project
fields = ['title', 'featured_images']
Then in the view you have to grab multiple entries:
# views.py
def createProject(request):
form = ProjectForm()
if request.method == 'POST':
form = ProjectForm(request.POST, request.FILES.getlist('featured_images'))
if form.is_valid():
project = form.save(commit=False)
project.save()
context = {'form':form}
return render(request, 'projects/project_form.html', context)
Please let me know if that works.
Upvotes: 1