Skip to content

Commit

Permalink
Merge 6938103 into 6192357
Browse files Browse the repository at this point in the history
  • Loading branch information
gaqzi committed Apr 10, 2014
2 parents 6192357 + 6938103 commit 80c0a44
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 2 deletions.
1 change: 1 addition & 0 deletions AUTHORS.rst
@@ -1,6 +1,7 @@
Alejandro Varas <alej0varas@gmail.com>
Alex Orange <crazycasta@gmail.com>
Andy Freeland <andy@andyfreeland.net>
Björn Andersson <ba@sanitarium.se>
Carl Meyer <carl@dirtcircle.com>
Curtis Maloney <curtis@tinbrain.net>
Den Lesnov
Expand Down
3 changes: 2 additions & 1 deletion CHANGES.rst
Expand Up @@ -4,6 +4,8 @@ CHANGES
master (unreleased)
-------------------

* Add ``random_function`` to models, a factory used with ``FileField``.


2.0.3 (2014.03.19)
-------------------
Expand Down Expand Up @@ -240,4 +242,3 @@ master (unreleased)
-----

* Added ``QueryManager``

34 changes: 34 additions & 0 deletions docs/models.rst
Expand Up @@ -47,3 +47,37 @@ returns objects with that status only:
# this query will only return published articles:
Article.published.all()
random_filename
---------------

A factory for random filenames to be used with the ``FileField``'s
``upload_to`` parameter.

The extension from the uploaded file is taken and used for the newly
generated filename.

``random_filename`` can take two arguments:

``directory``:
The directory the uploaded file should be placed in. Defaults is blank.
``random_function``:
A function that takes one argument, filename, and returns a random/unique
representation of that filename.
Default is ``uuid.uuid4``.


An example filename: `assets/270ef3e7-2105-4986-a8fb-6ef715273211.png`.


.. code-block:: python
from django.db import models
from model_utils.models import random_filename
class Asset(models.Model):
name = models.CharField(max_length=50)
file = models.FileField(upload_to=random_filename('assets/'))
30 changes: 30 additions & 0 deletions model_utils/models.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
from os import path

from django.db import models
from django.utils.translation import ugettext_lazy as _
Expand Down Expand Up @@ -92,3 +93,32 @@ def add_timeframed_query_manager(sender, **kwargs):

models.signals.class_prepared.connect(add_status_query_managers)
models.signals.class_prepared.connect(add_timeframed_query_manager)


def random_filename(directory='', random_function=None):
"""Returns a function that generates random filenames for file
uploads. The new filename keeps the extension from the original
upload.
The default ``random_function`` is uuid.uuid4 which also will
ensure that the filename is reasonably unique.
Args:
directory: The directory the file should be uploaded to. Default: ''
random_function: A function that takes a filename as an argument
to generate a unique filename. The file extension will be added
to the value returned from this function. Default: uuid.uuid4
Returns: a function that can be used with Django's ``FileField``.
"""
if not random_function:
from uuid import uuid4
random_function = lambda _: uuid4()

def random_name(instance, filename):
return path.join(directory,
'{0}{1}'.format(random_function(filename),
path.splitext(filename)[1]))

return random_name
48 changes: 47 additions & 1 deletion model_utils/tests/tests.py
Expand Up @@ -17,7 +17,8 @@
from model_utils import Choices, FieldTracker
from model_utils.fields import get_excerpt, MonitorField, StatusField
from model_utils.managers import QueryManager
from model_utils.models import StatusModel, TimeFramedModel
from model_utils.models import (StatusModel, TimeFramedModel,
random_filename)
from model_utils.tests.models import (
InheritanceManagerTestRelated, InheritanceManagerTestGrandChild1,
InheritanceManagerTestGrandChild1_2,
Expand Down Expand Up @@ -1871,3 +1872,48 @@ def test_child_fields_not_tracked(self):
self.name2 = 'test'
self.assertEqual(self.tracker.previous('name2'), None)
self.assertTrue(self.tracker.has_changed('name2'))


class RandomFilenameFactoryTest(TestCase):
def setUp(self):
self.random = lambda _: 'HELLO-IR-UUID'
self.random_name = random_filename(
'test_dir',
self.random
)

def test_upload_directory_should_be_configurable(self):
random_name = random_filename('new-directory/', self.random)
self.assertEqual(random_name(None, 'hello.jpeg'),
'new-directory/HELLO-IR-UUID.jpeg')

def test_should_return_a_random_name(self):
self.assertEqual(self.random_name(None, 'hello.jpeg'),
'test_dir/HELLO-IR-UUID.jpeg')

def test_should_use_the_current_extension_from_passed_in_filename(self):
self.assertEqual(self.random_name(None, 'foo.bar'),
'test_dir/HELLO-IR-UUID.bar')

def test_should_work_with_no_file_extension(self):
self.assertEqual(self.random_name(None, 'hello'),
'test_dir/HELLO-IR-UUID')

def test_allow_the_random_function_to_be_replaced(self):
random_name = random_filename('test_dir', lambda _: 'UNIQUE')
self.assertEqual(random_name(None, 'hello.jpeg'),
'test_dir/UNIQUE.jpeg')

def test_random_function_takes_the_filename_as_an_argument(self):
random_name = random_filename(
'test_dir',
lambda f: 'UNIQUE-{0}'.format(f)
)
self.assertEqual(random_name(None, 'hello.jpeg'),
'test_dir/UNIQUE-hello.jpeg.jpeg')

def test_default_random_function_accepts_parameter(self):
random_name = random_filename('test_dir')
filename = random_name(None, 'hello.jpeg')
self.assertTrue(filename.startswith('test_dir/'))
self.assertTrue(filename.endswith('.jpeg'))

0 comments on commit 80c0a44

Please sign in to comment.