I want to let the user upload multiple images per post. Similarly to an e-commerce platform with multiple images per product. But till now the images are not sent to the database.
That’s my code so far:
models.py:
class Project(models.Model):
title = models.CharField(max_length=200)
describtion = models.TextField(null=True, blank=True)
class ProjectImage(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
image = models.FileField(upload_to="products")
forms.py:
class ProjectForm(ModelForm):
image = forms.ImageField(widget=ClearableFileInput(attrs={'multiple':True}))
class Meta:
model = Project
fields = ['title', 'describtion']
views.py:
def createProject(request):
form = ProjectForm()
if request.method == 'POST':
form = ProjectForm(request.POST)
images = request.FILES.getlist('image')
if form.is_valid():
project = form.save()
for i in images:
ProjectImage(project=project, image=i).save()
context = {'form':form}
return render(request, 'projects/project_form.html', context)
project_form.html:
<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 %}
<input type="submit" name="" id="">
</form>
settings.py:
STATIC_URL = '/static/'
MEDIA_URL = '/images/'
STATICFILES_DIRS = [
BASE_DIR / 'static'
]
MEDIA_ROOT = os.path.join(BASE_DIR, 'static/images')
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
project urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('projects.urls')),
]
app urls.py
urlpatterns = [
path("", views.createProject, name="create-project")
]
Advertisement
Answer
Issue:
You have made
ProjectFormwhich relates toProjectmodel, but theimagefield is inProjectImagemodel. So,imagefield is not even passing to the template and you also haven’t passed it infields=['title','describtion']inProjectFrom.You haven’t made configurations for saving the media files in project’s urls.py.
Solution:
You should make two forms in forms.py, first
ProjectFormwhich will get the data forProjectmodel and secondProjectImageFormwhich will get the list of images, then usingrequest.FILES.getlist('image')you can save images which relates to a particular instance one by one in loop as you tried to save.You should make media configurations in project’s urls.py
Try Below Code:
forms.py
from django import forms
from django.forms import ClearableFileInput
from .models import Project, ProjectImage
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = ['title', 'describtion']
class ProjectImageForm(forms.ModelForm):
class Meta:
model = ProjectImage
fields = ['image']
widgets = {
'image': ClearableFileInput(attrs={'multiple': True}),
}
views.py
from django.http import HttpResponse
from django.shortcuts import redirect, render
from .forms import ProjectImageForm, ProjectForm
from .models import Project, ProjectImage
def createProject(request):
form = ProjectForm()
form2 = ProjectImageForm()
if request.method == 'POST':
form = ProjectForm(request.POST)
form2 = ProjectImageForm(request.POST, request.FILES)
images = request.FILES.getlist('image')
if form.is_valid() and form2.is_valid():
title = form.cleaned_data['title']
describ = form.cleaned_data['describtion']
print(title, describ)
project_instance = Project.objects.create(
title=title, describtion=describ)
print('-------------------------------------------')
print(project_instance)
print('-------------------------------------------')
for i in images:
ProjectImage.objects.create(project=project_instance, image=i)
return redirect('thanks')
context = {'form': form, 'form2': form2}
return render(request, 'projects/project_form.html', context)
def thanks(request):
return HttpResponse('<h1>Form saved.</h1>')
project_form.html or template file:
<form class="form" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{form.title.label_tag}}
{{form.title}}
<br><br>
{{form.describtion.label_tag}}
{{form.describtion}}
<br><br>
{{form2.image.label_tag}}
{{form2.image}}
<br><br>
<input type="submit" name="" id="">
</form>
project’s urls.py
from django.conf.urls.static import static
from django.conf import settings
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('projects.urls'))
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT)
app’s urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('home.urls'))
]
Your models.py and settings.py can be remain same, but it’s recommended to use
MEDIA_URL = 'media/' and MEDIA_ROOT = os.path.join(BASE_DIR, 'media/'), then you should make nested folders inside it to save images or any files.
Note:You should always returnHttpResponseRedirectafter dealing with POST data, the tip is not specific to Django, it’s a good practice in general as stated in the tutorial4.
Note:Function based views are generally written insnake_casenot, you may change it tocamelCasecreate_projectfrom.createProject
Note:Add/at the end of upload_to asupload_to='products/'in FileField inProjectImagemodel.