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 photologue, sortedm2m, django.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 datetime
from django.utils.text import slugify
from 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 serializers
from photologue.models import PhotoSize
from 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 photologue, sortedm2m, django.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
Top comments (0)