django custom file storage path - django

i would like to dynamically set the file storage path from the view regardless of the actual media_root path. is this possible. i have looked into custom storage objects and i am aware of the custom upload_to method call. currently i have a method that is called when my ImageField model upload_to is specified. this lets me change the directory within media_root. i have tried to do something similar with an overriden FileSystemStorage class but whatever it is set to i think it is bound before i can modify it within a view. if fileupload handler is the way to go i would be curious as to how to implement one.

If path that you specified in upload_to starts with / then it will be considered as absolute path. Through this you can set any path that you want regardless of MEDIA_ROOT.

you can do this in your view path = default_storage.save(filePath, ContentFile(file))
where filePath is file path where you need to store, and file is the file which is uploaded by user. this function will return you the path by storing file.
file = request.FILES['filee']
filePath = '%s/%s' % ('path/to/directory', file.name)
file = file.read()
path = default_storage.save(filePath, ContentFile(file))

Related

How to add a function that creates a link to os.path.join(BASE_DIR, 'media/') in settings.py django?

I use froala editor to add text to the model and I want to change the folder where the images will be loaded from the form field.
My function to create a path looks like this:
#utilities.py
def article_img_directory_path(instance, filename):
article_title = slugify(instance.title, allow_unicode=True)
return 'images/{0}/{1}'.format(article_title, filename)
I don't know how to add this function to the editor's path correctly:
#settings.py
from articles.utilities import article_img_directory_path
FROALA_UPLOAD_PATH = os.path.join(BASE_DIR, 'media/')+str(article_img_directory_path)
Images are loaded and displayed, but as a result, the loading path is wrong
I ain't got the reputation to comment, but see are you using your models to save those files?
If yes,
use the upload_to attribute of FileField in your model and point it to article_img_directory_path, then automatically, all your files will be saved in your desired location.
If No,
I don't clearly get what you are trying to accomplish here, but
os.path.join(base_dir, 'media/') returns you the string path of the media folder.
You are trying to concatenate the bound method of your function here, not even invoking the function by using (). You gotta invoke the function by calling it and passing the arguments there. But I doubt that it will work from settings.py where you cannot access any instance.
So, I suggest you to look at the FileField and upload_to here https://docs.djangoproject.com/en/3.2/ref/models/fields/#:~:text=def%20user_directory_path(instance%2C%20filename)%3A%0A%20%20%20%20%23%20file%20will%20be%20uploaded%20to%20MEDIA_ROOT/user_%3Cid%3E/%3Cfilename%3E%0A%20%20%20%20return%20%27user_%7B0%7D/%7B1%7D%27.format(instance.user.id%2C%20filename)%0A%0Aclass%20MyModel(models.Model)%3A%0A%20%20%20%20upload%20%3D%20models.FileField(upload_to%3Duser_directory_path)

How to change the default media url for a in django file field?

I am using django-sendfile to protect certain files.
The problem is that when the file is displayed on the frontend it uses the MEDIA_ROOT location which is not where the file actually is.
The actual link (that checks permissions) that I want to use is:
http://127.0.0.1:8000/leave/supporting_doc/57
yet the link displayed is:
http://127.0.0.1:8000/media/leave/user_2/2018-09-06-logo.png
The field in the model is:
supporting_doc = models.FileField(
upload_to=user_leave_directory_path,
storage=SENDFILE_STORAGE,
blank=True,
validators=[FileExtensionValidator(ACCEPTED_FILE_TYPES)]
)
The upload to method give a link relative to MEDIA_ROOT:
def user_leave_directory_path(instance, filename):
'''Get the user directory for leave supporting document'''
# file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
return 'leave/user_'\
f'{instance.user.id}/{instance.start_date}-{filename}'

django save imagefile in static dir

I have some images i retrieve from http://image.eveonline.com/ that i want to save. Now part of those will be static, while others might change.
Instead of pulling all static images from their site, i want to do this when they are needed (if they are not saved yet)
How can i tell my model to save the imagefield in static or in media
Example model:
class ItemIcon(models.Model):
item = models.ForeignKey("items.Item")
size = models.IntegerField(choices=settings.IMAGE_SIZES)
url = models.URLField(unique=True) #The url to the evesite
image = models.ImageField(upload_to="static/images/items/")
(this model should be saved in static)
you need to set your MEDIA_URL and MEDIA_ROOT in your settings.py and then define your image field like this:
image = models.ImageField(upload_to="items")
Note that you dont need to add the whole path to upload_to attribute, Django will append the absolute path for you. And you need to have a folder named items inside your media folder.
If you want to save images in STATIC_ROOT, try this:
from django.conf import settings
fs = FileSystemStorage(location=settings.STATIC_ROOT)
class ItemIcon(models.Model):
item = models.ForeignKey("items.Item")
size = models.IntegerField(choices=settings.IMAGE_SIZES)
url = models.URLField(unique=True) #The url to the evesite
image = models.ImageField(upload_to="items",storage=fs)

upload_to attribute seems not to be used while saving a FileField

I can't get my FileField's url set to what I want.
My model is defined by
class MyModel(models.Model):
pdf_file = models.FileField(upload_to="reports", null=True, blank=True)
# more stuff
and I create an instance using:
myModel = MyModel()
myModel.pdf_file = "some_file.pdf"
myModel.save()
myModel.pdf_file.url returns <MEDIA_URL>/some_file.pdf, while I would expect it to be <MEDIA_URL>/reports/some_file.pdf, because of the upload_to attribute.
What am I missing?
EDIT
I first tried to set a File object instead of a string but it duplicates my file with a _<duplication_num> appended to it, so I first create my file in a tmp folder, and delete it:
myModel.pdf_file = File(open(TMP_FILE_PATH + filename))
myModel.save()
# now that the file is saved to its final location, delete tmp
filepath = os.path.abspath(TMP_FILE_PATH + filename)
os.remove(filepath)
upload_to is used for uploading, You're assigning the string name directly. upload_to takes action only when you create a FileField object (by uploading from a form).
You can read the documentation here
upload_to is a directory relative to your project root where the files you upload are meant to be stored. But you are not assigning it a file, you are assigning it a string, which seems to be causing your FileField to assume you have a file named some_file.pdf in your MEDIA_ROOT.
Repeat: assigning a filename (string) makes FileField to ignore the path defined in upload_to and takes the given string as the real path.
Good luck :)

Renaming files in Django FileField

I know that there is a very similar thread here, but I cannot find the solution to my problem.
I need to rename a file which is save in django models.FileField
I tried this
os.rename(old_path, new_path)
mod.direct_file = File(open(new_path))
mod.save()
And this
mod.direct_file.save(new_path, File(open(old_path)))
os.remove(old_path)
And many other ways, but nothing seemed to help. A new file is created in all ways, however, data in filefield does not change at all.
EDIT: SOLVED
os.rename(old_path, new_path)
cursor = connection.cursor()
cursor.execute("UPDATE mods_mod SET direct_file = %s WHERE id = %s", [new_name, mod.id])
transaction.commit_unless_managed()
I don't think you need to use raw SQL for this. I think you need to rename the file using the os facility, then set the model's FileField name to the new name. Maybe something like:
os.rename(model.direct_file.path, new_path)
model.direct_file.name = new_name
model.save()
new_name = 'photos_preview/' + str(uuid.uuid1())
os.rename(photo.image_preview.path, settings.MEDIA_ROOT + new_name)
photo.image_preview.name = new_name
photo.save()
The current Django documentation states:
"When you access a FileField on a model, you are given an instance of FieldFile as a proxy for accessing the underlying file." See docs for further reading.
Instead of using the Python File object to open the file, you should use FieldFile.open() to open the file, then manipulate the file's path accordingly. Afterward, save the model object, and the changes to the path should persist.
I came across this issue when I had blobs saved into django with no file extension, and I wanted to correct that. Best used when looping over a filtered queryset.
You cannot change instance.picture.path, and trying to access instance.picture.file.* will give an error because accessing it will try to open the old file. Setting instance.picture.name will still not let you access instance.picture.file.*, even after saving.
You can simply set the ImageField object itself to the location and all will work:
(Tested with django 1.10)
import imghdr
import os
from django.db import models
class MyModel(models.Model):
picture = models.ImageField()
instance = MyModel.objects.first()
if os.path.exists(instance.picture.path):
extension = imghdr.what(instance.picture.path)
os.rename(instance.picture.path, instance.picture.path + '.' + extension)
instance.picture = instance.picture.name + '.' + extension
instance.save()
You may use the following:
Suppose 'obj' is the django object that you want to rename. Then do this:
obj.file_field_name.name = new_name
obj.save()
It seems changing filename of a binary in FileField is pretty unflexible according to the django docs. It contains the path from Media root.
That points out to a name attr that is reflecting the path, not just the filename itself. Docs: This is the way that django model can find the file