djangowagtailwagtail-streamfieldwagtail-snippet

wagtail set auto focal-point during upload


I notice we can set custom image model on wagtail here: https://docs.wagtail.io/en/v2.9/advanced_topics/images/custom_image_model.html

I am trying to add an automated focal point during upload max max 200*220px.

I tried a lot following its above documentation.

from django.db import models

from wagtail.images.models import Image, AbstractImage, AbstractRendition

class CustomImage(AbstractImage):
    # Add any extra fields to image here

    # eg. To add a caption field:
    # caption = models.CharField(max_length=255, blank=True)

    admin_form_fields = Image.admin_form_fields + (
        # Then add the field names here to make them appear in the form:
        # 'caption',
    )


class CustomRendition(AbstractRendition):
    image = models.ForeignKey(CustomImage, on_delete=models.CASCADE, related_name='renditions')

    class Meta:
        unique_together = (
            ('image', 'filter_spec', 'focal_point_key'),

Can anyone please help me get it done setting up custom focal point?

Thanks Anamul


Solution

  • You probably do not need a custom image model to achieve this goal, Django has a built in system called signals. This lets you listen to creation & editing (plus others) of any existing Django model and modify the data before it is saved to the DB.

    A good example of this in use already in Wagtail is the feature detection system, which will automatically add a focal point on save if faces are detected.

    You can see how this has been implemented in the source code, wagtail/images/signal_handlers.py.

    You may need to understand how to build up a focal point, depending on how you want to calculate it but basically you need to call set_focal_point on your image instance. This method must be supplied an instance of a Rect which can be found in the source at images/rect.py.

    It is important to understand how to call your signal handlers registration function, I found this Stack Overflow answer to be helpful. However, it might be simpler to just add it to your wagtail_hooks.py file as you know it will be run at the right time (when the app is ready & models are loaded.

    You can read more at the Django docs for app.ready() if you would prefer not to rely on the wagtail_hooks.py approach.

    Example Implementation

    myapp/signal_handlers.py
    from django.db.models.signals import pre_save
    
    from wagtail.images import get_image_model
    from wagtail.images.rect import Rect
    
    
    def pre_save_image_add_auto_focal_point(instance, **kwargs):
        # Make sure the image doesn't already have a focal point
        # add any other logic here based on the image about to be saved
    
        if not instance.has_focal_point():
            # this will run on update and creation, check instance.pk to see if this is new
    
            # generate a focal_point - via Rect(left, top, right, bottom)
            focal_point = Rect(15, 15, 150, 150)
    
            # Set the focal point
            instance.set_focal_point(focal_point)
    
    
    def register_signal_handlers():
        # important: this function must be called at the app ready
    
        Image = get_image_model()
    
        pre_save.connect(pre_save_image_add_auto_focal_point, sender=Image)
    
    
    myapp/wagtail_hooks.py
    from .signal_handlers import register_signal_handlers
    
    
    register_signal_handlers()