django-versatileimagefield
django-versatileimagefield copied to clipboard
Documentation doesn't cover setting 'admin_thumbnail' property in ModelAdmin
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...