Почему путь self.filefield_field.path экземпляра модели django не совпадает с путем, по которому был загружен файл
Я создаю приложение на django (Django==3.1.14).
Приложение должно позволять пользователю загружать zip-файлы (в частности, это shapefiles).
Я хочу, чтобы django загружал и управлял этими файлами, храня их в модели, имеющей поле FileField для размещения файла.
Я хочу, чтобы каждый файл загружался по определенному пути, например, так:
MEDIA_ROOT/shp/<timestamp>/<file_name>.zip
ПРИМЕЧАНИЕ: временная метка имеет точность в несколько секунд.
в моей модели есть поле shp_file_folder_path
, значение которого должно быть равно пути к папке, в которой хранится <file_name>.zip
, поэтому
MEDIA_ROOT/shp/<timestamp>
Теперь я написал следующую модель
def generate_current_timestamp():
return datetime.datetime.now().strftime("%Y%m%d%H%M%S")
def generate_uploaded_shp_file_relpath():
UPLOADED_SHP_FILES_DIR = 'shp'
uploaded_shp_files_relpath_from_media_root = UPLOADED_SHP_FILES_DIR
timestamp = generate_current_timestamp()
current_file_subfolder = timestamp
# relative path of the folder which will contain the uploaded file
uploaded_shp_file_relpath = os.path.join(
uploaded_shp_files_relpath_from_media_root,
current_file_subfolder
)
return uploaded_shp_file_relpath
# Create your models here.
class Shp(models.Model):
name = models.CharField(max_length=50)
description = models.CharField(max_length=1000, blank=True)
shp_file = models.FileField(upload_to=generate_uploaded_shp_file_relpath())
# this is a file, but in postgres is represented as path
uploaded_date = models.DateField(default=datetime.date.today, blank=True)
shp_file_folder_path = models.CharField(default='undefined', max_length=1000, blank=True)
# this must be nor visible nor editable by admins.
# this default value is not required to be correct because it will be overwritten by the save method
def __str__(self):
return self.name
def clean(self):
super().clean()
if not self.shp_file.name.endswith('.zip'):
raise ValidationError('The file must have .zip extension.')
def save(self, *args, **kwargs):
# update value of shp_file_folder_path
print("Name:", self.shp_file.name)
print("Path:", self.shp_file.path)
print("URL:", self.shp_file.url)
print("Size:", self.shp_file.size)
print("File:", self.shp_file.file)
self.shp_file_folder_path = os.path.dirname( self.shp_file.path )
super().save(*args, **kwargs)
и в настройках у меня есть
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
в моем файле admin.py у меня есть
class ShpAdmin(admin.ModelAdmin):
fields = ('name', 'description', 'shp_file', 'uploaded_date')
readonly_fields = ('shp_file_folder_path',)
admin.site.register(Shp, ShpAdmin)
Этот код позволяет мне загружать файлы в директории, как я хочу,
но, просматривая таблицу для модели shp
(shp_shp
) через psql
, я вижу, что значение, хранящееся в поле shp_file_folder_path
, равно .../myapp/media
.
Это происходит потому, что self.shp_file.path
является .../myapp/media/myfile.zip
; на самом деле блок отпечатков показывает
Name: C_Jamoat.zip
Path: .../myapp/media/C_Jamoat.zip
URL: /media/C_Jamoat.zip
Size: 4048647
File: C_Jamoat.zip
Я ожидал, что это будет .../myapp/media/shp/<timestamp>/myfile.zip
, где файл действительно был сохранен (я проверил на своей ОС).
Почему это не так?
Удивительно, но при повторном просмотре shp_shp через sql, в поле shp_file
, похоже, каким-то образом появилась информация о правильном пути.
select shp_file from shp_shp where id=89;
shp_file
---------------------------------
shp/20240628195348/C_Jamoat.zip
Но тогда как возможно, что ни один атрибут instance.shp_file
не имеет информации о правильном пути?
Значение self.shp_file.path
и других атрибутов self.shp_file
было оценено неверно, поскольку они оцениваются последовательно только после сохранения экземпляра.
Поэтому решение состоит в том, чтобы сохранить объект дважды:
1 - первый раз позволил django автоматически обновлять атрибуты self.shp_file
2 - второй раз обновить значение self.shp_file_folder_path
в соответствии с правильно обновленным значением self.shp_file.path
решается изменением
def save(self, *args, **kwargs):
# update value of shp_file_folder_path
print("Name:", self.shp_file.name)
print("Path:", self.shp_file.path)
print("URL:", self.shp_file.url)
print("Size:", self.shp_file.size)
print("File:", self.shp_file.file)
self.shp_file_folder_path = os.path.dirname( self.shp_file.path )
super().save(*args, **kwargs)
до
def save(self, *args, **kwargs):
self.shp_file_folder_path = os.path.dirname( self.shp_file.path )
# call the save method, in order to allow django to update the attributes values of self.shp_file
# update value of shp_file_folder_path
print("Name:", self.shp_file.name)
print("Path:", self.shp_file.path)
print("URL:", self.shp_file.url)
print("Size:", self.shp_file.size)
print("File:", self.shp_file.file)
self.shp_file_folder_path = os.path.dirname( self.shp_file.path )
super().save(*args, **kwargs)