cft

Photologue : Nice Image Management for Django with DRF Integration

A powerful image management and gallery application for the Django web framework. Upload photos, group them into galleries, apply effects such as watermarks.w


user

Javid Mougamadou

2 years ago | 7 min read

Concepts

A powerful image management and gallery application for the Django web framework. Upload photos, group them into galleries, apply effects such as watermarks.

Dependencies

  • Django.
  • Pillow.
  • Django-sortedm2m. (Automatically installed with django-photologue)

Installation

The easiest way to install Photologue is with pip; this will give you the latest version available on PyPi:

pip install django-photologue

Getting Started

Prerequisites

Ensure that you have set MEDIA_URL and MEDIA_ROOT in setting.py :

#settings.py
MEDIA_URL = /media/MEDIA_ROOT = /data/media/

Configuration

1) Add photologuesortedm2mdjango.contrib.sites in INSTALLED_APPS and SITE_ID=1.

#settings.py
SITE_ID = 1

INSTALLED_APPS = [
...

'django.contrib.sites',

# Django Image Library - Photologue
'photologue',
'sortedm2m',]

2) Apply photologue migrations

python manage.py migrate photologue

3) Register photologue urls in urls.py

urlpatterns = [
...
# Photologue Urls
path('photologue/', include('photologue.urls', namespace='photologue')),]

Models

Add the photologue Photo field in your model :

image = models.ForeignKey(
'photologue.Photo',
null=True,
blank=True,
on_delete=models.SET_NULL,)

Then apply migrations.

python manage.py makemigrations
python manage.py migrate

Example of model :

class BlogPost(models.Model):
uuid = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
help_text="The uuid4 primary key of the post",
)
title = models.CharField(
max_length=255,
help_text="The title of the post",
)
content = models.TextField(
blank=True,
help_text="The content of the post",
)
publishers = models.ManyToManyField(
'auth.User',
related_name='posts',
help_text="The maintainers of the pods",
)
preview_image = models.ForeignKey(
'photologue.Photo',
null=True,
blank=True,
on_delete=models.SET_NULL,
help_text="The preview image of the appx",
)

def __str__(self):
return f'Appx: {self.name}'

Gallery

You can add a Gallery field which is a M2M of Photo field.

gallery = models.ForeignKey(
'photologue.Gallery',
null=True,
blank=True,
on_delete=models.SET_NULL,)

Photo Sizes

Go to the Django Admin Interface, then you can create new PhotoSize instances.
When a Photo instance will be created, an image will be created for each photo sizes with automatic crop and quality compression.

Here is examples of photo sizes :

  • thumbnail (100 x 100)
  • small (320 x 200)
  • medium (640 x 400)
  • large (960 x 600)

Accessors

The base of Photologue is the Photo model. When an instance is created, methods will be automatically added in order to retrieve photos at various photosizes. E.g. if you have an instance of Photo called image, then the following methods will have been added automatically:

preview_image.get_small_url()preview_image.get_medium_url()preview_image.get_large_url()preview_image.get_raw_url()

These can be used in a custom template to display a thumbnail, e.g.:

<a href="{{ preview_image.image.url }}">
<img src="{{ preview_image.get_raw_url }}" alt="{{ preview_image.title }}"></a>

Utils

Here is an function that take an image and produce a Photo instance :

from datetime import datetimefrom django.utils.text import slugifyfrom photologue.models import Photo


def create_photo(image):
title = f'{datetime.now()}'
slug = slugify(title)
photo = Photo.objects.create(
title=title,
slug=slug,
image=image,
)
return photo

DRF Integration

Mixins

You can define this method create_mixin_image_serializer() which produces a MixinSerializer based on a image field name.

from rest_framework import serializersfrom photologue.models import PhotoSizefrom collections import OrderedDict


def _get_image_factory(image_field_name, photo_size):
def get_image(self, obj):
request = self.context['request']
if request and hasattr(obj, image_field_name):
image = getattr(obj, image_field_name)
if image is not None:
get_url_method = getattr(image, f'get_{photo_size}_url')
url = get_url_method()
return request.build_absolute_uri(url)
return None
return get_image


def create_mixin_image_serializer(image_field_name):
photo_sizes = PhotoSize.objects\
.filter(id__gte=3)\
.values_list('name', flat=True)

class ImageSerializerMixin(serializers.ModelSerializer):
pass

ImageSerializerMixin._declared_fields = OrderedDict([
(f'{image_field_name}_{photo_size}', serializers.SerializerMethodField())
for photo_size in photo_sizes
])

for photo_size in photo_sizes:
setattr(
ImageSerializerMixin,
f'get_{image_field_name}_{photo_size}',
_get_image_factory(image_field_name, photo_size),
)
return ImageSerializerMixin

Serializers

1) You can instanciate a mixin image serializer with the field preview_image :

PreviewImageSerializerMixin = create_mixin_image_serializer('preview_image')

Otherwise you can manually create the mixin image serializer :

class PreviewImageSerializerMixin(serializers.ModelSerializer):
preview_image_small = serializers.SerializerMethodField()
preview_image_medium = serializers.SerializerMethodField()
preview_image_large = serializers.SerializerMethodField()
preview_image_raw = serializers.SerializerMethodField()

def get_preview_image_small(self, obj):
request = self.context['request']
if request and hasattr(obj, 'preview_image'):
image = getattr(obj, 'preview_image')
if image is not None:
url = image.get_small_url()
return request.build_absolute_uri(url)
return None

def get_preview_image_medium(self, obj):
request = self.context['request']
if request and hasattr(obj, 'preview_image'):
image = getattr(obj, 'preview_image')
if image is not None:
url = image.get_medium_url()
return request.build_absolute_uri(url)
return None

def get_preview_image_large(self, obj):
request = self.context['request']
if request and hasattr(obj, 'preview_image'):
image = getattr(obj, 'preview_image')
if image is not None:
url = image.get_large_url()
return request.build_absolute_uri(url)
return None

def get_preview_image_raw(self, obj):
request = self.context['request']
if request and hasattr(obj, 'preview_image'):
image = getattr(obj, 'preview_image')
if image is not None:
url = image.get_raw_url()
return request.build_absolute_uri(url)
return None

2) Here is an exemple of serializer :

class BlogPostSerializer(PreviewImageSerializerMixin):
"""
Serializer for BlogPost instance
"""
class Meta:
model = BlogPost
fields = ('uuid', 'title', 'content',
'preview_image_small', 'preview_image_medium',
'preview_image_large', 'preview_image_raw')

3) Define create() and update() in case of uploading image :

def create(self, validated_data):
preview_image = validated_data.pop('preview_image', None)
instance = super(BlogPostSerializer, self).create(validated_data)

# Save image field
if preview_image:
instance.preview_image = create_photo(preview_image)

instance.save()
return instance

def update(self, instance, validated_data):
preview_image = validated_data.pop('preview_image', None)
instance = super(BlogPostSerializer, self).update(instance, validated_data)

# Update image field
if preview_image:
instance.preview_image = create_photo(preview_image)

instance.save()
return instance

Notes for Production

You should integrate the photologue library step by step.

1) Install the package.

pip install django-photologue

2) Add photologuesortedm2mdjango.contrib.sites in INSTALLED_APPS

INSTALLED_APPS = [
...

'django.contrib.sites',

# Django Image Library - Photologue
'photologue',
'sortedm2m',]
python manage.py migrate photologue

3) Apply photologue migrations

python manage.py migrate photologue

4) If you would like to replace an ImageField with a Photo in your models.py, you have to follow theses steps :

  • Remove ImageField in your models.py
  • Apply migrations
  • Add Photo in your models.py
  • Apply migrations

If you would like to keep your images, you should keep two fields:

  • Add Photo in your models.py
  • Apply migrations
  • Instanciates Photo
  • Remove ImageField in your models.py
  • Apply migrations

Links

Upvote


user
Created by

Javid Mougamadou


people
Post

Upvote

Downvote

Comment

Bookmark

Share


Related Articles