How do I make a gallery from a zip archive in wagtail?
I
have a project where I want to make wagtail
pages
where users can upload a zip
archive of images in the admin section. Then I want to unzip this archive and put each unzipped image into the image library connected to this page :
import urllib.parse
import os
import zipfile
from django.db import models
from django.conf import settings
from wagtail.wagtailcore.models import Page, Orderable
from wagtail.wagtailcore.fields import RichTextField
from wagtail.wagtailadmin.edit_handlers import FieldPanel, InlinePanel
from wagtail.wagtailsearch import index
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
from wagtail.wagtailimages.models import Image
from taggit.models import TaggedItemBase
from modelcluster.fields import ParentalKey
from modelcluster.contrib.taggit import ClusterTaggableManager
from .forms import PhotoEventPageForm
from utils import get_paginated_pages, get_all_tags
...
class PhotoEventPage(Page):
photos = models. FileField(upload_to=directory_path)
photos_unpacked = models. BooleanField(default=False)
...
base_form_class = PhotoEventPageForm
def main_image(self):
gallery_item = self.gallery_images.first()
if gallery_item:
return gallery_item.image
else:
return None
def save(self, *args, **kwargs):
if os.path.isfile(self.photos.path) and not self.photos_unpacked:
with zipfile. ZipFile(self.photos.path, "r") as zip_ref:
for file_name in zip_ref.namelist():
zip_ref.extract(
file_name,
path=os.path.dirname(self.photos.path))
# now i have image file, this line prints True:
# print(os.path.isfile(os.path.join(os.path.dirname(self.photos.path), file_name)))
# so now I have saved image and i want to create Image gallery,
# I've tried this code:
# img = Image()
# img.save()
# gallery_img = PhotoEventPageGalleryImage(
# image_id=img.id, page_id=self.id)
# gallery_img.save()
# but it's not working
self.photos_unpacked = True
return super().save(*args, **kwargs)
class PhotoEventPageGalleryImage(Orderable):
page = ParentalKey(PhotoEventPage, related_name='gallery_images')
image = models. ForeignKey(
'wagtailimages. Image', on_delete=models. CASCADE, related_name='+'
)
caption = models. CharField(blank=True, max_length=250)
panels = [
ImageChooserPanel('image'),
FieldPanel('caption'),
]
But it doesn’t work, and when I uncomment this code, I don’t know how to create wagtail.wagtailimages.models.Image
from the image I unzipped instance:
img = Image()
img.save()
gallery_img = PhotoEventPageGalleryImage(
image_id=img.id, page_id=self.id)
gallery_img.save()
I GET NOT NULL CONSTRAINT FAILED: wagtailimages_image.width.
So maybe there is a way to do this?
After some googling, thanks to Paulo Scardine, also checked:
this
I came up with the following code:
from django.core.files.images import ImageFile
from wagtail.wagtailcore.models import Page, Orderable
from wagtail.wagtailcore.fields import RichTextField
from wagtail.wagtailadmin.edit_handlers import FieldPanel, InlinePanel
from wagtail.wagtailsearch import index
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
from wagtail.wagtailimages.models import Image
from taggit.models import TaggedItemBase
...
class PhotoEventPage(Page):
...
def save(self, *args, **kwargs):
if os.path.isfile(self.photos.path) and not self.photos_unpacked:
with zipfile. ZipFile(self.photos.path, "r") as zip_ref:
for file_name in zip_ref.namelist():
zip_ref.extract(
file_name,
path=os.path.dirname(self.photos.path))
# print(os.path.isfile(os.path.join(os.path.dirname(self.photos.path), file_name)))
image_file = open(
os.path.join(
os.path.dirname(
self.photos.path), file_name), "rb")
image = Image(
title="Image title",
file=ImageFile(image_file, name=file_name),
)
image.save()
image_file.close()
gallery_img = PhotoEventPageGalleryImage(
image_id=image.id, page_id=self.id)
gallery_img.save()
self.photos_unpacked = True
return super().save(*args, **kwargs)
It’s weird, I mean it does work and I can save PhotoEventPage
without errors and the image is extracted, but I don’t know why after saving any items in self.gallery_images
.
Solution
I need to create a wagtail_hooks.py
file, and, as described in docs, Define the Hook there:
Model .py:
from wagtail.images.models import Image
...
class ExtenedWagtailImage(Image):
filepath = models. FilePathField()
class PhotoEventPageGalleryImage(Orderable):
page = ParentalKey(PhotoEventPage, related_name='gallery_images')
image = models. ForeignKey(
ExtenedWagtailImage, on_delete=models. CASCADE, related_name='+'
)
caption = models. CharField(blank=True, max_length=250)
panels = [
ImageChooserPanel('image'),
FieldPanel('caption')
]
wagtail_hooks.py:
import os
import zipfile
from django.core.files.images import ImageFile
from django.db import IntegrityError
from wagtail.core import hooks
from .models import PhotoEventPageGalleryImage, ExtenedWagtailImage as EWI
@hooks.register('after_edit_page')
@hooks.register('after_create_page')
def create_gallery(request, page):
if page.__class__.__name__ == 'PhotoEventPage':
with zipfile. ZipFile(page.photos.path, "r") as zip_ref:
for file_name in zip_ref.namelist():
image_file_path = os.path.join(
os.path.dirname(page.photos.path), file_name)
if not os.path.isfile(image_file_path):
zip_ref.extract(
file_name,
path=os.path.dirname(page.photos.path))
with open(image_file_path, "rb") as image_file:
try:
image_obj, created = EWI.objects.get_or_create(
filepath=image_file_path,
defaults={
'file': ImageFile(image_file, name=file_name),
'title': file_name}
)
# if it's not an image
except IntegrityError:
continue
photo_event_amount = len(
PhotoEventPageGalleryImage.objects.filter(
page_id=page.id, image_id=image_obj.id))
if created or photo_event_amount == 0:
PhotoEventPageGalleryImage(
image_id=image_obj.id, page_id=page.id).save()