83
You can use a callable in the upload_to
argument rather than using custom storage. See the docs, and note the warning there that the primary key may not yet be set when the function is called. This can happen because the upload may be handled before the object is saved to the database, so using ID
might not be possible. You might want to consider using another field on the model such as slug. E.g:
import os
def get_upload_path(instance, filename):
return os.path.join(
"user_%d" % instance.owner.id, "car_%s" % instance.slug, filename)
then:
photo = models.ImageField(upload_to=get_upload_path)
6
https://docs.djangoproject.com/en/stable/ref/models/fields/#django.db.models.FileField.upload_to
def upload_path_handler(instance, filename):
return "user_{id}/{file}".format(id=instance.user.id, file=filename)
class Car(models.Model):
...
photo = models.ImageField(upload_to=upload_path_handler, storage=fs)
There is a warning in the docs, but it shouldnβt affect you since weβre after the User
ID and not the Car
ID.
In most cases, this object will not
have been saved to the database yet,
so if it uses the default AutoField,
it might not yet have a value for its
primary key field.
- [Django]-Error: "dictionary update sequence element #0 has length 1; 2 is required" on Django 1.4
- [Django]-What's the difference between CharField and TextField in Django?
- [Django]-Django request get parameters
6
You can use lambda function as below, take note that if instance is new then it wonβt have the instance id, so use something else:
logo = models.ImageField(upload_to=lambda instance, filename: 'directory/images/{0}/{1}'.format(instance.owner.id, filename))
- [Django]-Custom validation in Django admin
- [Django]-How to use Django to get the name for the host server?
- [Django]-How to force Django models to be released from memory
3
My solution is not elegant, but it works:
In the model, use a the standard function that will need the id/pk
def directory_path(instance, filename):
return 'files/instance_id_{0}/{1}'.format(instance.pk, filename)
in views.py save the form like this:
f=form.save(commit=False)
ftemp1=f.filefield
f.filefield=None
f.save()
#And now that we have crated the record we can add it
f.filefield=ftemp1
f.save()
It worked for me.
Note: My filefield in models and allowed for Null values. Null=True
- [Django]-Any thoughts on A/B testing in Django based project?
- [Django]-Composite primary key in django
- [Django]-Django URL Redirect
2
Well very late to the party but this one works for me.
def content_file_name(instance, filename):
upload_dir = os.path.join('uploads',instance.albumname)
if not os.path.exists(upload_dir):
os.makedirs(upload_dir)
return os.path.join(upload_dir, filename)
Model like this only
class Album(models.Model):
albumname = models.CharField(max_length=100)
audiofile = models.FileField(upload_to=content_file_name)
- [Django]-How to test Django's UpdateView?
- [Django]-Django Rest Framework β Updating a foreign key
- [Django]-Django project base template
1
There are two solutions on DjangoSnippets
- Two-stage save: https://djangosnippets.org/snippets/1129/
- Prefetch the ID (PostgreSQL only): https://djangosnippets.org/snippets/2731/
- [Django]-What does this Django regular expression mean? `?P`
- [Django]-Get virtualenv's bin folder path from script
- [Django]-How to lookup django session for a particular user?
0
This guy has a way to do dynamic path. The idea is to set your favourite storage and customise βupload_to()β parameter with a function.
Hope this helps.
- [Django]-How to have a Python script for a Django app that accesses models without using the manage.py shell?
- [Django]-Django's SuspiciousOperation Invalid HTTP_HOST header
- [Django]-Django model object with foreign key creation
0
I found out a different solution, which is dirty, but it works. You should create a new dummy model, which is self synchronized with the original one. Iβm not proud of this, but didnβt find another solution. In my case I want to upload files, and store each in a directory named after the model id (because Iβll generate there more files).
the model.py
class dummyexperiment(models.Model):
def __unicode__(self):
return str(self.id)
class experiment(models.Model):
def get_exfile_path(instance, filename):
if instance.id == None:
iid = instance.dummye.id
else:
iid = instance.id
return os.path.join('experiments', str(iid), filename)
exfile = models.FileField(upload_to=get_exfile_path)
def save(self, *args, **kwargs):
if self.id == None:
self.dummye = dummyexperiment()
self.dummye.save()
super(experiment, self).save(*args, **kwargs)
Iβm very new in python and in django, but it seems like ok for me.
another solution:
def get_theme_path(instance, filename):
id = instance.id
if id == None:
id = max(map(lambda a:a.id,Theme.objects.all())) + 1
return os.path.join('experiments', str(id), filename)
- [Django]-Multiple Database Config in Django 1.2
- [Django]-Accepting email address as username in Django
- [Django]-How can I set two primary key fields for my models in Django?
0
As the primary key (id) may not be available if the model instance was not saved to the database yet, I wrote my FileField subclasses which move the file on model save, and a storage subclass which removes the old files.
Storage:
class OverwriteFileSystemStorage(FileSystemStorage):
def _save(self, name, content):
self.delete(name)
return super()._save(name, content)
def get_available_name(self, name):
return name
def delete(self, name):
super().delete(name)
last_dir = os.path.dirname(self.path(name))
while True:
try:
os.rmdir(last_dir)
except OSError as e:
if e.errno in {errno.ENOTEMPTY, errno.ENOENT}:
break
raise e
last_dir = os.path.dirname(last_dir)
FileField:
def tweak_field_save(cls, field):
field_defined_in_this_class = field.name in cls.__dict__ and field.name not in cls.__bases__[0].__dict__
if field_defined_in_this_class:
orig_save = cls.save
if orig_save and callable(orig_save):
assert isinstance(field.storage, OverwriteFileSystemStorage), "Using other storage than '{0}' may cause unexpected behavior.".format(OverwriteFileSystemStorage.__name__)
def save(self, *args, **kwargs):
if self.pk is None:
orig_save(self, *args, **kwargs)
field_file = getattr(self, field.name)
if field_file:
old_path = field_file.path
new_filename = field.generate_filename(self, os.path.basename(old_path))
new_path = field.storage.path(new_filename)
os.makedirs(os.path.dirname(new_path), exist_ok=True)
os.rename(old_path, new_path)
setattr(self, field.name, new_filename)
# for next save
if len(args) > 0:
args = tuple(v if k >= 2 else False for k, v in enumerate(args))
kwargs['force_insert'] = False
kwargs['force_update'] = False
orig_save(self, *args, **kwargs)
cls.save = save
def tweak_field_class(orig_cls):
orig_init = orig_cls.__init__
def __init__(self, *args, **kwargs):
if 'storage' not in kwargs:
kwargs['storage'] = OverwriteFileSystemStorage()
if orig_init and callable(orig_init):
orig_init(self, *args, **kwargs)
orig_cls.__init__ = __init__
orig_contribute_to_class = orig_cls.contribute_to_class
def contribute_to_class(self, cls, name):
if orig_contribute_to_class and callable(orig_contribute_to_class):
orig_contribute_to_class(self, cls, name)
tweak_field_save(cls, self)
orig_cls.contribute_to_class = contribute_to_class
return orig_cls
def tweak_file_class(orig_cls):
"""
Overriding FieldFile.save method to remove the old associated file.
I'm doing the same thing in OverwriteFileSystemStorage, but it works just when the names match.
I probably want to preserve both methods if anyone calls Storage.save.
"""
orig_save = orig_cls.save
def new_save(self, name, content, save=True):
self.delete(save=False)
if orig_save and callable(orig_save):
orig_save(self, name, content, save=save)
new_save.__name__ = 'save'
orig_cls.save = new_save
return orig_cls
@tweak_file_class
class OverwriteFieldFile(models.FileField.attr_class):
pass
@tweak_file_class
class OverwriteImageFieldFile(models.ImageField.attr_class):
pass
@tweak_field_class
class RenamedFileField(models.FileField):
attr_class = OverwriteFieldFile
@tweak_field_class
class RenamedImageField(models.ImageField):
attr_class = OverwriteImageFieldFile
and my upload_to callables look like this:
def user_image_path(instance, filename):
name, ext = 'image', os.path.splitext(filename)[1]
if instance.pk is not None:
return os.path.join('users', os.path.join(str(instance.pk), name + ext))
return os.path.join('users', '{0}_{1}{2}'.format(uuid1(), name, ext))
- [Django]-Django custom management commands: AttributeError: 'module' object has no attribute 'Command'
- [Django]-How to customize activate_url on django-allauth?
- [Django]-Strings won't be translated in Django using format function available in Python 2.7
0
MEDIA_ROOT/
/company_Company1/company.png
/shop_Shop1/shop.png
/bikes/bike.png
def photo_path_company(instance, filename):
# file will be uploaded to MEDIA_ROOT/company_<name>/
return 'company_{0}/{1}'.format(instance.name, filename)
class Company(models.Model):
name = models.CharField()
photo = models.ImageField(max_length=255, upload_to=photo_path_company)
def photo_path_shop(instance, filename):
# file will be uploaded to MEDIA_ROOT/company_<name>/shop_<name>/
parent_path = instance.company._meta.get_field('photo').upload_to(instance.company, '')
return parent_path + 'shop_{0}/{1}'.format(instance.name, filename)
class Shop(models.Model):
name = models.CharField()
photo = models.ImageField(max_length=255, upload_to=photo_path_shop)
def photo_path_bike(instance, filename):
# file will be uploaded to MEDIA_ROOT/company_<name>/shop_<name>/bikes/
parent_path = instance.shop._meta.get_field('photo').upload_to(instance.shop, '')
return parent_path + 'bikes/{0}'.format(filename)
class Bike(models.Model):
name = models.CharField()
photo = models.ImageField(max_length=255, upload_to=photo_path_bike)
- [Django]-Error: could not determine PostgreSQL version from '10.3' β Django on Heroku
- [Django]-Django β How to set default value for DecimalField in django 1.3?
- [Django]-Serializer call is showing an TypeError: Object of type 'ListSerializer' is not JSON serializable?
0
You can override modelβs save
method:
def save_image(instance, filename):
instance_id = f'{instance.id:03d}' # 001
return f'{instance_id}-{filename.lower()}' # 001-foo.jpg
class Resource(models.Model):
photo = models.ImageField(upload_to=save_image)
def save(self, *args, **kwargs):
if self.id is None:
photo = self.photo
self.photo = None
super().save(*args, **kwargs)
self.photo = photo
if 'force_insert' in kwargs:
kwargs.pop('force_insert')
super().save(*args, **kwargs)
- [Django]-Django: Adding "NULLS LAST" to query
- [Django]-Prepopulate Django (non-Model) Form
- [Django]-Laravel's dd() equivalent in django
0
The method will be
def user_directory_path(field_name):
def upload_path(instance, filename):
year = datetime.now().year
name, ext = instance.user, os.path.splitext(filename)[1]
return f'photos/{year}/{instance._meta.model_name}s/{instance.user}/{field_name}_{name}{ext}'
return upload_path
And in your models you can have as many ImageField as you like. example
photo = models.ImageField(upload_to=user_directory_path('photo'), null=True, blank=True,)
passport_photo = models.ImageField(upload_to=user_directory_path('passport_photo'), null=True, blank=True,)
- [Django]-Use Python standard logging in Celery
- [Django]-Custom validation in Django admin
- [Django]-What is a "django backend"?