Создание поста и загрузка нескольких файлов в Django
У меня есть таблица с животными. Для каждого животного у меня есть несколько изображений. Я хотел бы, чтобы пользователь мог зарегистрировать новое животное и добавить к нему несколько изображений в одной форме. На данный момент никаких записей не создается, и я не понимаю, почему. Я бьюсь над этим уже несколько часов. Я знаю, что могу добиться этого, используя наборы форм, но предпочел бы что-то вроде этого.
Я подозреваю, что это происходит из-за вида, но не понимаю, почему.
Вот моя неработающая система:
upl/models.py
from django.db import models
from django.core.exceptions import ValidationError
def validate_img_extension(value):
if not value.name.endswith(".jpg", ".jpeg", ".png"):
raise ValidationError("Only .jpg, .jpeg og .png allowed.")
class Animal(models.Model):
title = models.CharField(max_length=200)
description = models.TextField(null=True, blank=True)
class AnimalImage(models.Model):
animal = models.ForeignKey(Animal, on_delete=models.CASCADE)
image = models.FileField(upload_to="products/", validators=[validate_img_extension])
upl/views.py
from django.shortcuts import redirect, render
from .forms import AnimalImageForm, AnimalForm
from .models import Animal, AnimalImage
def createAnimal(request):
animalform = AnimalForm()
imageform = AnimalImageForm()
if request.method == "POST":
animalform = AnimalForm(request.POST)
imageform = AnimalImageForm(request.POST or None, request.FILES or None)
images = request.FILES.getlist("image")
if animalform.is_valid() and imageform.is_valid():
title = animalform.cleaned_data["title"]
describ = animalform.cleaned_data["description"]
animal_instance = Animal.objects.create(title=title, description=describ)
animal_instance.save()
for i in images:
AnimalImage.objects.create(animal=animal_instance, image=i)
return redirect("uplhome")
context = {"animalform": animalform, "imageform": imageform}
return render(request, "upl/animal_form.html", context)
def uplhome(request):
return render(request, "upl/uplhome.html")
forms.py
from django import forms
from django.forms import ClearableFileInput
from .models import Animal, AnimalImage
class MultipleFileInput(forms.ClearableFileInput):
allow_multiple_selected = True
class AnimalForm(forms.ModelForm):
class Meta:
model = Animal
fields = ["title", "description"]
class AnimalImageForm(forms.ModelForm):
class Meta:
model = AnimalImage
fields = ["image"]
widgets = {"image": MultipleFileInput()}
upl/urls.py
from django.urls import path, include
from upl import views
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path("uplhome", views.uplhome, name="uplhome"),
path("createanimal", views.createAnimal, name="createanimal"),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
upl/animal_form.html
<h1>Animal Form </h1>
<form class="form" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{animalform.title.label_tag}}
{{animalform.title}}
<br><br>
{{animalform.description.label_tag}}
{{animalform.description}}
<br><br>
{{imageform.image.label_tag}}
{{imageform.image}}
<br><br>
<button type="submit">Submit</button>
</form>
<a href="{% url 'uplhome' %}">Animal Home</a>
Как говорит документация Django,
Если вы хотите загружать несколько файлов с помощью одного поля формы, создайте подкласс виджета поля и установите его атрибут класса allow_multiple_selected в True.
Для того чтобы все эти файлы были проверены формой (и значение поля включало их все), вам также придется создать подкласс FileField.
У вас уже есть код для виджета, поэтому осталось подклассифицировать только FileField - опять же, пример из документации:
class MultipleFileField(forms.FileField):
def __init__(self, *args, **kwargs):
kwargs.setdefault("widget", MultipleFileInput())
super().__init__(*args, **kwargs)
def clean(self, data, initial=None):
single_file_clean = super().clean
if isinstance(data, (list, tuple)):
result = [single_file_clean(d, initial) for d in data]
else:
result = [single_file_clean(data, initial)]
return result
Затем вы можете использовать этот класс в своей форме следующим образом:
class AnimalImageForm(forms.ModelForm):
image = MultipleFileField()
class Meta:
model = AnimalImage
fields = ["image"]
И вам придется настроить свой валидатор, чтобы он позволял проверять несколько значений:
def validate_img_extension(files_list):
for file in files_list:
if not file.name.endswith((".jpg", ".jpeg", ".png")):
raise ValidationError("Only .jpg, .jpeg og .png allowed.")