Very good examples @jerch. I’ll give yet another one, using functools partial.
from functools import partial
# Update your wrapper function to accept a target
def wrapper(instance, filename, target):
...
# on your model
head_image = models.ImageField(upload_to=partial(wrapper, target="head")
body_image = models.ImageField(upload_to=partial(wrapper, target="body")
And I’ll jump into this thread to point out that there is no requirement, and very little benefit, to ensuring that the file name as it is stored in the file system have any relationship to how that file is represented to a user. (And in fact, creates a source of problems if you ever need to relocate those files to a different storage environment.)
A database purist would tell you that overloading the semantics of a file name with “process-based meaning” is a violation of the principles of normalization.
That type of information would be most appropriately stored as meta-data associated with that field.
For example, all our systems handling uploaded files have a separate “FileHandler” model. This model contains fields for things like the original file name, file type (e.g. “csv”, “txt”, “pdf”, etc), upload date, uploaded by, description, etc.
Then, each model that can be associated with an uploaded file either has a one-to-one relationship with that model (if only one file is appropriate), or a many-to-many relationship.
As a result, we don’t know or care what the underlying directory structure - or file names - that are used for the storage of those files. The file field tracks that, which is all that is important for that field. Everything else is managed by the metadata associated with that file.
interesting thanks for the explanation, the site Im building is for like 300-400 users (it wont grow much more than that), does it make sense to setup metadata for these?
maybe something like
class Image(model.Model)
user = OneToOneField(etc)
image = ImageField(etc)
metadata = OneToOneFIeld(ImageHandler, etc)
class ImageHandler(model.Model)
size = IntField
extension = StringField ("jpeg, png, etc")
description = StringField("body/head")
Just a positive note, coming from other python web frameworks (flask, fastapi), the community in django is phenomenal, easy to get help on dumb questions, makes it very productive and easy to get stuff done.
The degree to which you adhere to any specific set of architectural standards is always a judgement call. All such standards and principles are a trade-off of sorts. In essence, you’re making a decision between short-term objectives and long-term viability. (“You can pay me now or you can pay me later…”) And those are decisions that can only be made in the context of your business model.
But in your specific case, what model is using that Image model? Understanding the larger context may be helpful here.
Here, the ImageHandler is effectively a direct replacement for the ImageField in your Image model. It has become a separate model to allow for the explict management of your metadata associated with that field.
In this case, you would not actually need an explicit “file type” field, because those semantics are defined by the one-to-one relationship of the Image field. (By definition, the ImageHandler being referred to by the head_image is, by definition, a file type of head.)