I am building a web page where a blog author can write content and upload images. Using an image field, I am allowing the author to upload multiple images and with some Javascript logic, I am displaying the images before upload. Under each image, I have a checkbox that indicates if that is the main image of the post (e.g. the first to show in the slideshow and used as thumbnail of the post itself).
On this page I am showing two forms with a shared submit button and it works fine.
My problems begin when I try to save the image and also indicate if it is the main one.
My images model has multiple helpful methods, thumbnail property for Django admin and a save method that will set all images for a post to false, before editing/adding a new main_image. I had to include a commit argument logic because of my experiments. Please ignore it. The model includes 3 fields: the image, the main flag and a foreign key to the post as follows:
class PostImages(models.Model):
"""
Post images for the blog app.
"""
image = models.ImageField(
verbose_name=_("Изображение"),
upload_to='blog/images/',
blank=False,
null=False,
default='blog/images/default.jpg'
)
post = models.ForeignKey(
to=Post,
on_delete=models.CASCADE,
verbose_name=_('Статия')
)
main_image = models.BooleanField(
default=False,
verbose_name=_('Основно изображение')
)
class Meta:
verbose_name = _('Изображение')
verbose_name_plural = _('Изображения')
permissions = [
('can_manage_post_image', _('Може да управлява изображенията на публикациите')),
('can_add_post_image', _('Може да добавя изображения към публикациите')),
]
def __str__(self):
return f"{self.post.title} - {self.image}"
def get_absolute_url(self):
return f"/blog/post/image/{self.id}"
def get_edit_url(self):
return f"/blog/post/image/{self.id}/edit"
def get_delete_url(self):
return f"/blog/post/image/{self.id}/delete"
def get_image(self):
return mark_safe(f'<img src="{self.image.url}" width="100" height="100" />')
def get_image_url(self):
return self.image.url
@property
def thumbnail_preview(self):
if self.image:
return mark_safe('<img src="{}" width="400" height="auto" />'.format(self.image.url))
return ''
def save(self, *args, **kwargs):
if self.main_image and kwargs.get("commit", True):
PostImages.objects.filter(post=self.post).update(main_image=False)
kwargs.pop("commit", None)
super().save(*args, **kwargs)
I am also using a Model form. In the model form I do not ask for the post, because it is not yet created. My idea was to fill the post in the save phase following a process of:
class PostImagesForm(forms.ModelForm):
"""
Form for uploading images for posts.
"""
image = forms.ImageField(
label='Снимка',
widget=forms.ClearableFileInput(
attrs={
'class': 'form-control',
'multiple': True,
}
)
)
main_image = forms.BooleanField(
label='Основна снимка',
required=False,
widget=forms.CheckboxInput(
# attrs={
# 'class': 'form-control',
# }
)
)
class Meta:
model = PostImages
fields = ['image', 'main_image']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['image'].required = False
def save(self, commit=True):
"""
Save the form and return the created or edited post.
"""
post_image = super().save(commit=False)
post_image.save(commit=commit)
return post_image
def clean(self):
"""
Clean the form and return the cleaned data.
"""
cleaned_data = super().clean()
return cleaned_data
So far, so .... good I assume.
So here are the questions:
I tried to use the form itself, but it misses the post instance.
I tried to iterate through the files but they are empty.
I tried to iterate through the form fields, but I do not see all uploaded files.
If someone ever needs this:
I found multiple problems that I think might be useful to share.
enctype="multipart/form-data"
from my form tag. (oops)"data:sjdfhsdkjbfksd"
in my identifier for the checkbox. So again a rookie mistake. I detected it when I started slapping console.log()
to everything an anything.sources
.var
statements, but be ready to be screamed at by a JS guy.