django-versatileimagefield icon indicating copy to clipboard operation
django-versatileimagefield copied to clipboard

Documentation doesn't cover setting 'admin_thumbnail' property in ModelAdmin

Open Routhinator opened this issue 7 years ago • 0 comments

I cannot get this field to work with the 'admin_thumbnail' property definition in my custom DjangoUserAdmin class:

class MemberAdmin(DjangoUserAdmin):
    """Extend DjangoUserAdmin for our custom user class"""
    form = MemberChangeForm
    add_form = MemberCreationForm
    admin_thumbnail = AdminThumbnail(image_field='avatar["square_50_url"]')
    list_display = ('admin_thumbnail', 'username', 'created', 'updated', 'last_login', '__str__',)
    list_display_links = ('username',)
    fieldsets = (
        (
            None,
            {'fields': ('username', 'password')}
        ),
        (
            trans('Personal info'),
            {'fields': ('email', 'avatar', 'background', 'bio', 'homepage', 'location', 'language', 'signature')}
        ),
        (
            trans('Permissions'),
            {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
    )
    add_fieldsets = (
        (
            None,
            {'fields': ('username', 'email', 'password1', 'password2'), 'classes': ('wide',)}
        ),
    )

Key sets:

VERSATILEIMAGEFIELD_RENDITION_KEY_SETS = {
    'member_avatar': [
        ('full_size', 'url'),
        ('square_200_url', 'crop__200x200'),
        ('square_100_url', 'crop__100x100'),
        ('square_50_url', 'crop__50x50')
    ],
    'member_background': [
        ('full_size', 'url'),
        ('card_url', 'crop__260x140'),
        ('wide_url', 'crop__1100x160')
    ]
}

Member model:

class Member(AbstractBaseUser, PermissionsMixin, TimestampedModel):
    """
    Main Member model
    """

    username = models.CharField(db_index=True, max_length=255, unique=True)

    slug = models.SlugField(
        editable=False,
        help_text=trans("URL and SEO friendly lower-cased string."),
        unique=True
    )

    email = models.EmailField(blank=False, db_index=True, unique=True)

    is_active = models.BooleanField(default=True)

    is_staff = models.BooleanField(default=False)

    legacy_id = models.PositiveIntegerField(default=False)

    avatar_ppoi = PPOIField()

    avatar_width = models.PositiveIntegerField(
        'Avatar Width',
        blank=True,
        null=True
    )

    avatar_height = models.PositiveIntegerField(
        'Avatar Height',
        blank=True,
        null=True
    )

    avatar = VersatileImageField(
        upload_to="avatars",
        ppoi_field='avatar_ppoi',
        width_field='avatar_width',
        height_field='avatar_height',
        null=True
    )

    background_ppoi = PPOIField()

    background_width = models.PositiveIntegerField(
        'Background Width',
        blank=True,
        null=True
    )

    background_height = models.PositiveIntegerField(
        'Background Height',
        blank=True,
        null=True
    )

    background = VersatileImageField(
        upload_to="backgrounds",
        ppoi_field='background_ppoi',
        width_field='background_width',
        height_field='background_height',
        null=True
    )

    bio = models.TextField(
        null=True,
        blank=True,
        help_text=trans("")
    )

    homepage = models.CharField(
        max_length=255,
        null=True,
        blank=True,
        help_text=trans("")
    )

    location = models.CharField(
        null=True,
        blank=True,
        help_text=trans(""),
        max_length=255
    )

    language = LanguageField(
        help_text=trans("Your primary language or mother tongue."),
        default="en"
    )

    activation_key = models.UUIDField(
        unique=True,
        default=uuid.uuid4
    )

    signature = models.TextField(null=True, blank=True)

    # More fields required by Django when specifying a custom user model.

    # The `USERNAME_FIELD` property tells us which field we will use to log in.
    # In this case, we want that to be the email field.
    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']

    # Tells Django that the MemberManager class defined above should manage
    # objects of this type.
    objects = MemberManager()

    class Meta:
        permissions = [
            ("members_can_flag", "Can flag profiles for moderation"),
        ]
        verbose_name = trans('Member')
        verbose_name_plural = trans('Members')

    def __str__(self):
        """
        Returns a string representation of this `User`.

        This string is used when a `User` is printed in the console.
        """
        return self.email

    def save(self, *args, **kwargs):
        """Supers the default save routine to sanitize the input and generate the slug."""

        if self.pk is None and not self.background:
            random_image = get_random_image_from_directory(settings.STATIC_ROOT+'/images/covers/')
            self.background.save(
                str(self.username.lower()) + '_background' + get_file_extension(random_image.name),
                random_image,
                save=True
            )
            random_image.close()

        if self.pk is None and not self.avatar:
            random_image = get_random_image_from_directory(settings.STATIC_ROOT+'/images/avatars/')
            self.avatar.save(
                str(self.username.lower()) + '_avatar' + get_file_extension(random_image.name),
                random_image,
                save=True
            )
            random_image.close()

        max_length = Member._meta.get_field('slug').max_length
        self.slug = orig = slugify(self.username)[:max_length]

        for count in itertools.count(1):
            if not Member.objects.filter(slug=self.slug).exists():
                break

            # Truncate the original slug dynamically. Minus 1 for the hyphen.
            self.slug = "%s-%d" % (orig[:max_length - len(str(count)) - 1], count)

        super(Member, self).save(*args, **kwargs)

    def get_full_name(self):
        """
        This method is required by Django for things like handling emails.
        Typically, this would be the user's first and last name. Since we do
        not store the user's real name, we return their username instead.
        """
        return self.username

    def get_short_name(self):
        """
        This method is required by Django for things like handling emails.
        Typically, this would be the user's first name. Since we do not store
        the user's real name, we return their username instead.
        """
        return self.username


@receiver(models.signals.post_save, sender=Member)
def warm_member_avatar_images(sender, instance, **kwargs):
    """Ensures Member Avatar resizing is done post-save"""
    del kwargs
    member_avatar_warmer = VersatileImageFieldWarmer(
        instance_or_queryset=instance,
        rendition_key_set='member_avatar',
        image_attr='avatar',
        verbose=False
    )
    num_created, failed_to_create = member_avatar_warmer.warm()
    LOGGER.debug(
        'Avatars warmed for %s. Created: %s, Failed: %s',
        str(sender.username),
        str(num_created),
        str(failed_to_create)
    )


@receiver(models.signals.post_save, sender=Member)
def warm_member_background_images(sender, instance, **kwargs):
    """Ensures Member Background resizing is done post-save"""
    del kwargs
    member_background_warmer = VersatileImageFieldWarmer(
        instance_or_queryset=instance,
        rendition_key_set='member_background',
        image_attr='background',
        verbose=False
    )
    num_created, failed_to_create = member_background_warmer.warm()
    LOGGER.debug(
        'Backgrounds warmed for %s. Created: %s, Failed: %s',
        str(sender.username),
        str(num_created),
        str(failed_to_create)
    )

The docs don't cover this at all, and none of the attempts I've made to figure out the expected syntax work:

admin_thumbnail = AdminThumbnail(image_field='avatar["square_50_url"]')
admin_thumbnail = AdminThumbnail(image_field='avatar.image.square_50_url')
admin_thumbnail = AdminThumbnail(image_field='avatar.image.crop[50x50]')
admin_thumbnail = AdminThumbnail(image_field='avatar.image.crop.50x50')
admin_thumbnail = AdminThumbnail(image_field='avatar.square_50_url')
admin_thumbnail = AdminThumbnail(image_field='avatar.image.square_50_url')

Just using avatar works, but the admin is a mess. This should be documented. How do we actually access the correct property in the admin? The urls work in the API just fine...

Routhinator avatar Oct 03 '18 15:10 Routhinator