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 to- Projectmodel, but the- imagefield is in- ProjectImagemodel. So,- imagefield is not even passing to the template and you also haven’t passed it in- fields=['title','describtion']in- ProjectFrom.
- 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 for- Projectmodel and second- ProjectImageFormwhich will get the list of images, then using- request.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.
