Skip to content

Commit

Permalink
#227 Gen filename from it's contents hash (#234)
Browse files Browse the repository at this point in the history
* #227  Gen filename from it's contents hash

* #227  Fix comments and import style
  • Loading branch information
duker33 authored Dec 31, 2018
1 parent 11231f5 commit 311e814
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 15 deletions.
21 changes: 21 additions & 0 deletions images/migrations/0007_custom_image_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2018-12-27 10:04
from __future__ import unicode_literals

from django.db import migrations
import images.models


class Migration(migrations.Migration):

dependencies = [
('images', '0006_auto_20170418_1615'),
]

operations = [
migrations.AlterField(
model_name='image',
name='image',
field=images.models.ImageField(upload_to='', verbose_name='image'),
),
]
35 changes: 20 additions & 15 deletions images/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import hashlib
import os
import time

from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
Expand All @@ -9,20 +9,25 @@
from sorl.thumbnail import delete


# @todo #STB296:60m Generate image file name with hash.
# Now generation uses timestamp.
# It breaks media<->fixtures consistency.
# See comment for details:
# https://github.com/fidals/stroyprombeton/issues/296#issuecomment-447770194
# And test absolute filename generation.
def model_directory_path(instance, filename):
def generate_filename(old_name: str, slug: str) -> str:
_, file_extension = os.path.splitext(old_name)
return (slug or str(time.time())) + file_extension
# stayed here for migrations compatibility
def model_directory_path(*args, **kwargs):
return ''

result_filename = generate_filename(filename, instance.slug)
models_folder_name = type(instance.model).__name__.lower()
return '{}/{}/{}'.format(models_folder_name, instance.model.pk, result_filename)

class ImageField(thumbnail.ImageField):

def generate_filename(self, instance, filename):
"""Reloaded this method. See `django.db.models.fields.files.FileField`."""
f = getattr(instance, self.name).file
_, extension = os.path.splitext(filename)
file_hash = hashlib.md5(f.read()).hexdigest()
models_folder_name = type(instance.model).__name__.lower()
filename = '/'.join([
models_folder_name,
str(instance.model.pk),
file_hash + extension
])
return self.storage.generate_filename(filename)


class ImageManager(models.Manager):
Expand Down Expand Up @@ -67,7 +72,7 @@ class Meta:
slug = models.SlugField(max_length=400, blank=True, db_index=True, verbose_name=_('slug'))
description = models.TextField(default='', blank=True, verbose_name=_('description'))
created = models.DateField(auto_now_add=True, verbose_name=_('created'))
image = thumbnail.ImageField(upload_to=model_directory_path, verbose_name=_('image'))
image = ImageField(verbose_name=_('image'))
is_main = models.BooleanField(default=False, db_index=True, verbose_name=_('is main'))

def __str__(self):
Expand Down
19 changes: 19 additions & 0 deletions tests/images/tests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os.path
import string

from django.core.files.images import ImageFile
from django.db import models
Expand Down Expand Up @@ -94,3 +95,21 @@ def test_update_main_image(self):
self.assertTrue(another_image_model.is_main)
self.assertEquals(another_image_model.image, self.page.main_image)
another_image_model.delete()

def test_file_name(self):
"""ImageField should generate filename based on file's content hash."""

def is_md5(data: str) -> bool:
return (
all(s in (string.ascii_letters + string.digits) for s in data)
and len(data) == 32
)

model = create_image_model(self.page, self.IMG_PATH, slug='one-another')
# @todo #227:60m Explore additional hash.
# Now autosaved file has this pattern `<md5>_dTy2ykr.jpg`
# but should have this one: `<md5>.jpg`.
# ManifestStaticFilesStorage adds redundant hash after md5.
# Make the storage to remove this hash.
hash, _ = os.path.basename(model.image.file.file.name).split('_')
self.assertTrue(is_md5(hash))

2 comments on commit 311e814

@0pdd
Copy link
Collaborator

@0pdd 0pdd commented on 311e814 Dec 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Puzzle STB296-d24da08f disappeared from images/models.py, that's why I closed #227. Please, remember that the puzzle was not necessarily removed in this particular commit. Maybe it happened earlier, but we discovered this fact only now.

@0pdd
Copy link
Collaborator

@0pdd 0pdd commented on 311e814 Dec 31, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Puzzle 227-534526f4 discovered in tests/images/tests.py and submitted as #242. Please, remember that the puzzle was not necessarily added in this particular commit. Maybe it was added earlier, but we discovered it only now.

Please sign in to comment.