0

I am trying to set upload paths in Django ImageField with the upload_to attribute with similar to below.

Model

image = models.ImageField(upload_to=image_upload_location(image="logo"))

Function

def image_upload_location(filename, instance, *args, **kwargs):

  if image:
    defaultFolder = "images/default"
    logoFolder = "images/logo"
    generalFolder = "images/general"
    productsFolder = "images/products"
    if image == "logo":
      folder = logoFolder
    elif image == "general":
      folder = generalFolder
    elif image == "products":
      folder = "productsFolder"
    else:
      folder = defaultFolder

    return "%s/%s" % (folder, filename)

I get the following error:

TypeError: image_upload_location() missing 2 required positional arguments: 'filename' and 'instance'

I've tried to pass instance and filename but can't work out how to make this work. Any ideas on how to make this work so that I can use the same function for ImageField as I'm trying to follow the DRY principal by making a function to handle all the "set" locations.

I also don't want the date fields that Django adds.

[edit] The main thing I need to know here is how to pass the required variable "instance and filename" plus an additional variable "image" to the function from the model ImageField. :-)

Baz
  • 306
  • 1
  • 5
  • 12
  • What is `instance`? You are not using it anywhere in your function. – Selcuk Feb 13 '16 at 08:09
  • Selcuk, no I'm not in that part of the function. It is there to satisfy the required positional arguments. I will use instance in another part of the function later. The main thing I need to know here is how to pass the required variable "instance and filename" plus an additional variable "image" to the function from the model ImageField. – Baz Feb 14 '16 at 23:16

1 Answers1

1

You have both filename and instance as parameters of your function, which you are not passing. I recommend doing this instead, if they aren't required.

def image_upload_location(*args, **kwargs):
    filename=kwargs.pop('filename', 'DEFAULT')
    instance=kwargs.pop('instance', 'DEFAULT')

or give them defaults

def image_upload_location(filename='DEFAULT', instance='DEFAULT', *args, **kwargs):

[edit]

Also, you never instance/grab/set "Image" in your function. Either set it as a parameter, or pop it from kwargs.

[edit on 2016-01-15] - This is what I think you want.


image = models.ImageField(upload_to=image_upload_location(image_type='logo', filename='filename.jpg'))

or

image = models.ImageField(upload_to=image_upload_location(filename='filename.jpg'))

or

image = models.ImageField(upload_to=image_upload_location('filename.jpg', 'logo'))

See below for all the different combination examples


def image_upload_location(filename, image_type='', *args, **kwargs):
    if image_type=='logo':
        folder = 'images/logo'
    elif image_type=='general':
        folder = 'images/general'
    elif image_type=='products':
          folder = 'images/products'
    else:
          folder = 'images/default'

    return '%s/%s' % (folder, filename)

#All of the different ways to call it
print(image_upload_location('test.jpg')); #1 indexed argument, 1 default argument
print(image_upload_location('test2.jpg', 'logo')); #2 indexed arguments
print(image_upload_location('test3.jpg', image_type='logo')); #1 indexed argument, 1 named argument (indexed arguments can't come after named arguments)
print(image_upload_location(image_type='general', filename='test4.jpg')); #2 named arguments
print(image_upload_location(filename='test5.jpg', image_type='products')); #2 named arguments in a different order

Also, you don't need to include args and kwargs as parameters if you aren't using them.

Dakusan
  • 6,504
  • 5
  • 32
  • 45
  • I still require filename in the path as seen by the function return statement. The "Model" image="logo" is a folder name attribute determined at the model for the type of image it is. So I am trying to use a simple indicator at the model for each type of image so they get stored in certain directories. I really just need to know how to pass additional variables and the required variables to the function from the model. – Baz Feb 14 '16 at 23:08
  • The way python function parameter works is if you declare one, you HAVE to explicitly pass it (whether by name or index), or give it a default in the function definition. If you want a purely optional parameter that isnt listed in the function definition, then instead of putting it as a function arg, you can pop it off of the kwargs dictionary. So it's fine to have "filename", "instance", "image", or whatever as named parameters in the function definition, but you have to either pass them or give them a default. I'll try to rewrite what you have to do what I think you want. – Dakusan Feb 15 '16 at 06:24
  • I can get the image variable passed to the function without errors, but I can't work out how to pass or extract the instance and filename from the model to the function with the additional variables. – Baz Feb 16 '16 at 07:42
  • Why do you need to pass an instance to your function? What instance are you talking about? From a guess, is this what you are trying to do: Whenever an image is uploaded, it will automatically add an "upload_to" field to the database based upon the image-type (which is set statically per field), and the new filename. – Dakusan Feb 16 '16 at 17:30
  • I'm just gonna assume that is the case. What you need to do in your model is add the function "def save(self, *args, **kwargs):" with "upload_to=image_upload_location(filename=self.filename, image_type='logo')" and then "super(MODELNAME, self).save(*args, **kwargs)". This assumes you have a field named "filename". Also, replace "MODELNAME" with the name of your model. If this is what you want, I can edit my answer to include the code. – Dakusan Feb 16 '16 at 17:40
  • Yes I have static paths for images such as product images for a shop: (root/static/images/products/product/filename) or site wide such as a logo: (root/static/images/corporate/logo/filename). For other areas where there is user information like a profile picture, I want to use instance.user.id: (root/static/images/users/user_/profile_pic/filename). This is for a basic CMS site. I want to specify which folder to store the image in with a function that holds all the static paths which is called by image='product' or image='logo' or image='profile' which would return the above paths. – Baz Feb 22 '16 at 03:52