I have the following model and function to set the upload paths for either image files or other files (which will be audio files).
def upload_to(instance, filename):
# Check for image or sound file and put in appropriate directory
if isinstance(instance, models.ImageField):
print("detected image file")
return "x/img/%s" % (filename)
else:
print("detected non-image file")
return "x/aud/%s" % (filename)
class Tile(models.Model):
image = models.ImageField(upload_to=upload_to, default="../static/x/img/default.svg")
sound = models.FileField(upload_to=upload_to, null=True, blank=True)
The condition in upload_to is not correct, I realize, as instance is an instance of the tile object with all fields, but I'm not sure what to do in the upload_to function to find out if the file that was just uploaded by the user is an ImageField or FileField.
Based on Warren's suggestion, here is the solution I have have implemented in case anyone is curious, with less omitted for clarity:
def upload_image_to(instance, filename):
filename = filenamer_helper(instance, filename)
return f"x/img/{instance.user.id}/{instance.grid.id}/{filename}"
def upload_audio_to(instance, filename):
filename = filenamer_helper(instance, filename)
return f"x/aud/{instance.user.id}/{instance.grid.id}/{filename}"
# Adds normalized 3-digit number to beginning of filename
def filenamer_helper(instance, filename):
needs_tile_number = False
# Check if tile number is already in the filename
if f"{instance.tile_number}_" not in filename[0:4]:
needs_tile_number = True
# Prepend normalized ### tile number to filename if needed
if needs_tile_number:
if needs_tile_number and instance.tile_number < 10:
filename = f"00{instance.tile_number}_{filename}"
elif needs_tile_number and instance.tile_number < 100:
filename = f"0{instance.tile_number}_{filename}"
else:
filename = f"{instance.tile_number}_{filename}"
return filename
class Tile(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user_tiles")
grid = models.ForeignKey(Grid, on_delete=models.CASCADE, related_name="grid_tiles")
tile_number = models.IntegerField(default=0)
image = models.ImageField(upload_to=upload_image_to, default="../static/x/img/default.svg")
audio = models.FileField(upload_to=upload_audio_to, null=True, blank=True)
text = models.CharField(max_length=100, null=True, blank=True)
Could you have two separate upload_to
functions, one for each field/file type?
def upload_to_image(instance, filename):
return "x/img/%s" % (filename)
def upload_to_sound(instance, filename):
return "x/aud/%s" % (filename)
class Tile(models.Model):
image = models.ImageField(upload_to=upload_to_image, default="../static/x/img/default.svg")
sound = models.FileField(upload_to=upload_to_sound, null=True, blank=True)
In fact, if you just need separate directories for each field type, it looks like you should be able to pass that directly to the upload_to
field:
class Tile(models.Model):
image = models.ImageField(upload_to="x/img/", default="../static/x/img/default.svg")
sound = models.FileField(upload_to="x/aud/", null=True, blank=True)
and omit the callback functions entirely.