Как сделать одну форму для нескольких моделей с отношениями один-ко-многим в django?
У меня есть одна модель, имя которой Post. Она имеет два отношения "один-два-много" с моделями "PostImages" и "PostFiles". Я хочу создать только одну форму для обработки этого. Также я хочу, чтобы загрузка файлов и изображений не требовалась. И я сделал это, но мой код не запускает процесс валидации на "PostImagesForm" и "PostFilesForm", но он валидирует "PostForm".
models.py (я использую FileTypeValidator из пакета uload_validator, который отлично работает для миниатюр, так что с этим проблем нет)
class Post(models.Model):
title = models.CharField(max_length=60)
content = tinymce_models.HTMLField('content')
date = models.DateTimeField(default=timezone.now)
slug = AutoSlugField(populate_from='title', unique_with='date')
thumbnail_big = models.ImageField(upload_to='thumbnails/', validators=[FileTypeValidator(allowed_types=[VIEL])])
thumbnail_small = models.ImageField(upload_to='thumbnails/', blank=False, default=thumbnail_big)
def description(self):
return HTMLtoString(self.content)
def save(self, *args, **kwargs):
#THUMBNAIL COMPRESSION
#small thumbnail
if SizeOfImageGreaterThan(self.thumbnail_big, TQS.SMALL_THUMBNAIL_RESOLUTION):
thumbnail_compressed_small = Compress(self.thumbnail_big, TQS.SMALL_THUMBNAIL_RESOLUTION, TQS.SMALL_THUMBNAIL_QUALITY)
self.thumbnail_small = thumbnail_compressed_small
#big thumbnail
if SizeOfImageGreaterThan(self.thumbnail_big, TQS.BIG_THUMBNAIL_RESOLUTION):
thumbnail_compressed_big = Compress(self.thumbnail_big, TQS.BIG_THUMBNAIL_RESOLUTION, TQS.BIG_THUMBNAIL_QUALITY)
self.thumbnail_big = thumbnail_compressed_big
super(Post, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse("EatWell:Post", args=[self.slug])
class PostImages(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
big_image = models.ImageField(upload_to='images/', blank=True, null=True, validators=[FileTypeValidator(allowed_types=[VIEL])])
small_image = models.ImageField(upload_to='images/', blank=False, default=big_image)
def save(self, *args, **kwargs):
#IMAGE COMPRESSION
#small image
if SizeOfImageGreaterThan(self.big_image, IQS.SMALL_IMAGE_RESOLUTION):
small_image_compressed = Compress(self.big_image, IQS.SMALL_IMAGE_RESOLUTION, IQS.SMALL_IMAGE_QUALITY)
self.small_image = small_image_compressed
#big image
if SizeOfImageGreaterThan(self.big_image, IQS.BIG_IMAGE_RESOLUTION):
big_image_compressed = Compress(self.big_image, IQS.BIG_IMAGE_RESOLUTION, IQS.BIG_IMAGE_QUALITY)
self.big_image = big_image_compressed
super(PostImages, self).save(*args, **kwargs)
class PostFiles(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
file = models.FileField(upload_to='files/', blank=True, null=True)
forms.py (я использую crispy forms)
class PostCreationForm(forms.ModelForm):
class Meta:
model = models.Post
fields = ['title', 'content', 'date', 'thumbnail_big']
labels = {
'thumbnail_big': 'Thumbnail',
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form_method = 'POST'
self.helper = FormHelper(self)
self.helper.form_tag = False
self.helper.disable_csrf = True
self.helper.layout = Layout(
Fieldset(
'Create a post',
'title',
'content',
'date',
Field('thumbnail_big'), #accept=VIEL),
),
)
class PostImagesCreationForm(forms.ModelForm):
class Meta:
model = models.PostImages
fields = ['big_image']
labels = {'big_image' : ''}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form_method = 'POST'
self.helper = FormHelper(self)
self.helper.form_tag = False
self.helper.disable_csrf = True
self.helper.layout = Layout(
Fieldset(
'Upload images',
Field('big_image', multiple = True), #accept=VIEL),
),
)
class PostFilesCreationForm(forms.ModelForm):
class Meta:
model = models.PostFiles
fields = ['file']
labels = {'file' : ''}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form_method = 'POST'
self.helper = FormHelper(self)
self.helper.form_tag = False
self.helper.disable_csrf = True
self.helper.layout = Layout(
Fieldset(
'Upload files',
Field('file', multiple = True), #, accept = VEL
),
self.helper.add_input(Submit('submit','Submit')),
)
views.py
@login_required()
def PostCreationView(request):
post_form = forms.PostCreationForm()
images_form = forms.PostImagesCreationForm()
files_form = forms.PostFilesCreationForm()
if request.method == 'POST':
post_form = forms.PostCreationForm(request.POST, request.FILES)
files_form = forms.PostFilesCreationForm(request.FILES)
images_form = forms.PostImagesCreationForm(request.FILES)
images = request.FILES.getlist('big_image')
files = request.FILES.getlist('file')
if post_form.is_valid():
print('[INFO] PostCreationForm is valid')
post_form.save()
print('[INFO] PostCreationForm has been saved')
if images_form.is_valid(): #always returns True
print('[INFO] PostImagesCreationForm is valid')
for image in images:
models.PostImages.objects.create(post=post_form.save(), big_image=image)
print('[INFO] New table for image has been created')
else:
print('[THREAD] PostImagesCreationForm is not valid')
print(files_form.errors.as_data())
if files_form.is_valid(): #always returns True
print('[INFO] PostFilesCreationForm is valid')
for file in files:
models.PostFiles.objects.create(post=post_form.save(), file=file)
print('[INFO] New table for file has been created')
else:
print('[THREAD] PostFilesCreationForm is not valid')
print(files_form.errors.as_data())
return redirect('EatWell:Blog')
else:
print('[THREAD] PostFilesCreationForm is not valid')
print(post_form.errors.as_data())
context = {
'title' : 'Post Creation',
'tinymce' : True,
'post_form' : post_form,
'images_form' : images_form,
'files_form' : files_form
}
return render(request, 'postcreation/postcreation.html', context)
postcreation.html
{% extends 'postcreation/base.html' %}
{% load crispy_forms_tags %}
{% block body %}
<div class="container-sm border p-3">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% crispy post_form %}
{% crispy images_form %}
{% crispy files_form %}
</form>
</div>
<div class="container-sm border p-3 m-3">
<button class="btn btn-danger mr-2" onclick="location.href = '{% url 'PostCreation:Logout' %}'" ;>Log out</button><button class="btn btn-info"onclick="location.href = '{% url 'HomePage' %}'" ;>Return to the home page</button>
</div>
{% endblock body %}
Описанный там способ работает в основном так, как я хочу, но он не проверяет файлы и изображения в "PostFilesView" и в "ImageFileView", которые загружает пользователь