2

I have a class with a Django ImageField and I have been struggling to decide between two alternatives for storing that field's upload_to function. The first approach is pretty straightforward. The function is defined on the module level (c.f. https://stackoverflow.com/a/1190866/790075, https://stackoverflow.com/a/3091864/790075):

def get_car_photo_file_path(instance, filename):
    ext = filename.split('.')[-1]
    filename = "%s.%s" % (uuid.uuid4(), ext) # chance of collision <1e-50
    return os.path.join('uploads/cars/photos', filename)

class CarPhoto(models.Model):
    photo = models.ImageField(upload_to=get_car_photo_file_path)

This is simple and easy to understand, but pollutes the module scope by adding a function that is really only pertinent to the CarPhoto class.

In the second approach, I use the callable-class pattern to associate the function more closely with the CarPhoto class. This moves the upload_to function out of module scope but feels unnecessarily complicated.

class CarPhoto(models.Model):
    class getCarPhotoFilePath():
        # Either use this pattern or associate function with module instead of this class
        def __call__(self, instance, filename):
            ext = filename.split('.')[-1]
            filename = "%s.%s" % (uuid.uuid4(), ext) # chance of collision <1e-50
            return os.path.join('uploads/cars/photos', filename)

    photo = models.ImageField(upload_to=getCarPhotoFilePath())

I have seen suggestions for using the @staticmethod and @classmethod decorators (c.f. https://stackoverflow.com/a/9264153/790075), but I find that when I do this the function never executes and the filename ends up looking like: /path/to/file/<classmethod object>, with the method object embedded in the file path, which is certainly not intended!

Which of these is the preferred pattern? Is there a better way?

1
  • BTW you can avoid defining a function / method / class altogether and turn your get_car_photo_file_pathinto an inline lambda right inside ImageField invocation. But this probably be totally unreadable. Commented Oct 2, 2012 at 16:34

3 Answers 3

1

I would recommend that you:

import this

To me, this falls under the Zen of Python's section stating:

Simple is better than complex.
Complex is better than complicated.

I think your simple solution is better. But, your complex doesn't feel overly complicated. I think you will probably be ok either way. Just my two cents.

Sign up to request clarification or add additional context in comments.

2 Comments

I was hoping for something more concrete, but I can't say I don't agree with you!
Sorry. I tried to be pretty concrete and say that I think the simple solution (the first one) is better. And the reason why is that I think it is more closely adheres to the "Zen of Python". But, the solution with the Callable is not terrible. I just prefer the first one a bit.
0

There's a naming convention to prevent name pollution.

  • use _get_car_photo_file_path to mark your function as internal (though not hidden);
  • use __get_car_photo_file_path to prevent access to it from outside your class.

You can add a classmethod or staticmethod like this to your CarPhoto class, which is simpler than adding a callable class (the latter reminds me of Java's way to define an anonymous class for the sake of one method).

The name will cleanly show that _get_car_photo_file_path is an implementation detail and not a part of the interface, thus preventing pollution of class's namespace. Being CarPhoto's method, the function will not pollute module's namespace.

2 Comments

That's true, but how would you suggest I use that with either of the cases mentioned above?
Unfortunately this approach fails. See my response to @vartec's suggestion.
0

Currently in the code I'm working with we have the variation of the simplest one. The only difference is that since the function is intended for internal use, it's marked so with _ prefix.

def _get_car_photo_file_path(instance, filename):
    [...]

class CarPhoto(models.Model):
    photo = models.ImageField(upload_to=_get_car_photo_file_path)

However, I do believe this would be more Pythonic (or rather more OOP):

class CarPhoto(models.Model):

    @staticmethod
    def _get_file_path(instance, filename):
        [...]

    photo = models.ImageField(upload_to=_get_file_path) 

6 Comments

The second suggestion was what I tried first, but this fails because you don't have access to CarPhoto (the class) or self here. See comments stackoverflow.com/a/3091717/790075
@turtlemonvh: static method doesn't need self.
@turtlemonvh: I fail to see how you use self of class inside the function that calculates the file path. The instance parameter is never used in the function from your post.
@vartec True, but it does need the class, which is also not available
@9000 That's correct: I don't use self in the function. The name of the file is generated as a uuid and is not derived based on any property of the file itself. My point is not that I need self to do the work of the function, but that calling either a staticmethod or a classmethod requires access to either self or self.__class, both of which are unavailable.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.