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
ProjectForm
which relates toProject
model, but theimage
field is inProjectImage
model. So,image
field 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
ProjectForm
which will get the data forProject
model and secondProjectImageForm
which 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 returnHttpResponseRedirect
after 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_case
not, you may change it tocamelCase
create_project
from.createProject
Note:
Add/
at the end of upload_to asupload_to='products/'
in FileField inProjectImage
model.