Hi Clive,

On Oct/28/2020, Clive Bruton wrote:
> Thanks, that was very helpful. I had another pointer on this and found out
> that the easiest way to do this is just to change the ```file``` line in the
> class to:
> 
> ```
>     file = ImageField(_('image'), upload_to='images/items/%Y/%m/%d/%H/%M/%S')
> ````

I haven't used this method. I always pass a callable (passing a
function).

(I'm deleting a part of your message...)

> So, given that ```upload_path``` returns a string, I don't understand why I
> also have to concatenate the ```filename```:
> 
> ```
> def upload_path(instance, filename):
>       dtnow = datetime.now(timezone.utc)
>       dtstr = '{:%Y/%m/%d}'.format(dtnow)
>       dtstr = 'images/items/' + dtstr + '/' + filename
>       return dtstr
> ```
> In this case, if I do not concatenate ```filename``` then the uploaded file
> gets named with the last element of the date formatting and without a file
> extension, ie potentially like this:
> 
> ```images/items/2020/10/28/20/59/55```
> 
> rather than:
> 
> ```images/items/2020/10/28/20/59/55/image.jpg```

I see...

> The other thing I don't understand is how ```instance``` and ```filename```
> are passed to the function ```upload_path```. I would expect to do something
> like:
> 
> ```   file = ImageField(_('image'), upload_to=upload_path(instance,
> filename))```
> 
> But if I do that I get:
> 
> ```NameError: name 'instance' is not defined```
> 
> Sorry for so many more questions!

I see...

I'll explain two things that put together might help you.

What Django is doing (for now believe me, but keep reading to see it
yourself :-) ) is similar to this:
------------
def print_name(name):
    print(f'The name is {name}')

def print_short_name(name):
    print(f'Short name: {name[0:2]}')

def call_with_upper_parameter(function_to_call, param):
    param = param.upper()
    function_to_call(param)

for name in ['Clive', 'Jannett']:
    call_with_upper_parameter(print_name, name)
    call_with_upper_parameter(print_short_name, name)
------------

The output is:

The name is CLIVE
Short name: CL
The name is JANNETT
Short name: JA

Note that "call_with_upper_parameter" gets two parameters: a callable
and a parameter. It converts the parameter to upper case and
then calls the function_to_call with this parameter.

If you would do "print(function_to_call)" you would see "<function ... at
0x......>"

There is the Python function "callable" to know if a variable can be
called (so if it can be executed: pass parameter -or not- and with () )

Or in another way:
------------------
In [1]: def f():
   ...:     pass
   ...:

In [2]: name = 'Clive'

In [3]: callable(f)
Out[3]: True

In [4]: callable(name)
Out[4]: False

In [5]: f()

In [6]: name()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-8afa4fbf817e> in <module>()
----> 1 name()

TypeError: 'str' object is not callable

In [7]:
------------------

In my opinion: it takes a bit of effort the first times to understand
them. After this it stays for you for any language.

And now the Django part:
You are using ImageField. ImageField is a FileField: 
https://github.com/django/django/blob/master/django/db/models/fields/files.py#L370

FileField in the init saves the upload_to into self.upload_to: 
https://github.com/django/django/blob/966b5b49b6521483f1c90b4499c4c80e80136de3/django/db/models/fields/files.py#L240

Side note: if self.upload_to is a str at some point forces to be relative: 
https://github.com/django/django/blob/966b5b49b6521483f1c90b4499c4c80e80136de3/django/db/models/fields/files.py#L265

But here the interesting code: 
https://github.com/django/django/blob/966b5b49b6521483f1c90b4499c4c80e80136de3/django/db/models/fields/files.py#L308
In "generate_filename" is doing:
if it's a callable: it calls self.upload_to with the instance and the
filename
If it's not a callable (a str in your case): it executes
datetime.datetime.now().strftime(str(self.upload_to)) : so it's
interpolating the %Y, %m, etc. that you had used and uses it as a base
directory and then adds the filename (posixpath.join(dirname, filename))

Does this explain how it works? I think that your questions might be
answered with the callable code and the Django code.

(I think that you had read the documentation but here it is:
https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.FileField.upload_to
, it doesn't explain how it is implemented internally).

It might help you to use a Python debugger and see the steps as they
happen, variables, etc. (adding a breakpoint in the Django code and
inspect variables).

Let me know if it's not clear or if I explained something that you
didn't ask!

Cheers,

-- 
Carles Pina i Estany
https://carles.pina.cat

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/20201030110035.GA22706%40pina.cat.

Reply via email to