Django model with FileField -- dynamic 'upload_to' argument - django

I am using the model with FileField to deal with file uploading. Now the files can be uploaded successfully. However, there is one more small improvement I want to make, which is to create folder for the user with the username.
Here is the code I've tried
class UserFiles(models.Model):
user = models.OneToOneField(User)
file = models.FileField(upload_to='files/users/user.username/%Y_%m_%d/')
this would give the folder of 'user.username' instead of 'John'(one example of username)
I have also tried other ways like files/users/%user.username/%Y_%m_%d/
,but it would not give the folder with the user name. Not sure how the syntax should be or whether this is possible.
Can you give some suggestions on this?
Thank you very much for your help and explanation.

Instead of a string try passing a function:
def generate_filename(self, filename):
url = "files/users/%s/%s" % (self.user.username, filename)
return url
class UserFiles(models.Model):
user = models.OneToOneField(User)
file = models.FileField(upload_to=generate_filename)

Related

Passing The ID of a Field in another Field of the same model Django

Hello, I am trying to pass the ID of a Model in an Image Field URL(upload_to) and then access it Through a URL unique to The instance.
Here's What I did (Amature);
class User(models.Model):
serial = models.AutoField(primary_key=True)
profile = models.ImageField(upload_to=f"profiles/{serial}/")
But all I'm Getting is OSError.
I wanted to save the file to profiles/{serial}/ directory in the app.
So Every Instance of the Model has its own Directory. And Then access it Through host:port/api/users/{serial}/profile.jpg
My View Set is served through host:port/api/users
Is there a way I can Do it?
Any Help is Highly Appreciated. A Detailed Explaination is Even more Appreciated.
From https://docs.djangoproject.com/en/3.0/ref/models/fields/#django.db.models.FileField.upload_to
upload_to may also be a callable, such as a function. This will be called to obtain the upload path, including the filename. This callable must accept two arguments and return a Unix-style path (with forward slashes) to be passed along to the storage system. The two arguments are: instance and filename
So you can do something like this
def profile_handler(instance, file_name)
return f"profiles/{instance.id}/{file_name}"
class User(models.Model):
serial = models.AutoField(primary_key=True)
profile = models.ImageField(upload_to=profile_handler)
on model override save method like this
def save(self,*args, **kwargs):
self.fieldname=currentmodel.objects.last().id+1
super(Orders, self).save(*args, **kwargs)

Django how to access a uploaded file in views?

In my views.py file, there are some functions like follows:
def upload_file(request):
"""
upload a file and store it into the database,
and the file will be predicted in another view immediately.
"""
def predict(request):
"""
make prediction of the file uploaded in the 'upload_file' function.
"""
How to access the file which uploaded in upload_file function in the predict function? Here is my thought:
1. read the last row in the database, but this sounds a little silly.
2. use a cache system and retrieve the file from cache?
Is there any useful solution for this problem? please give me any hints or other resources.
Thanks for anyone's help.
As it is described here, you can acces the storage url as Follows :
Class MyModel(models.Model):
photo = models.ImageField()
model = MyModel.objects.get(pk=1) # for instance
model.photo.url
>>> 'https://media.example.com/mymodels/example_image.jpg'

Python & Django, UUID not unique

I'm developing an application using Django. I have a form in which the user uploads 3 different files (at least one). Then those files are sended to a home script that generates some result files. I want to store all those files in one directory, each directory name unique by form submission. I've look around on the Internet and I find the UUID technology. I installed the module of Django named django-uuid-upload-path. But when I submit my form, it is always the same uuid that is returned, looking like a UUID string. Here is my model where I'm using this module :
from django.db import models
from uuid_upload_path import uuid
class Analysis(models.Model):
uidDir = uuid()
dirFile = 'documents/%Y/%m/%d/' + str(uidDir)
structureFile = models.FileField(upload_to = dirFile)
I've tried to use upload_to from this module in this way :
from uuid_upload_path import upload_to
class Analysis(models.Model):
structureFile = models.FileFiels(upload_to = upload_to)
I've done this for my 3 FileFields and it gave me 3 different UUID on one form submission. The problem is now that my files are not in the same directory.
Here is my controller, where I upload the files submitted by the user :
def analysis(request):
if request.method == 'POST':
documents = Analysis.objects.all()
form = AnalysisForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Analysis(structureFile = request.FILES['structureFile'])
newdoc.save()
I've tried with the uuid module from Python but I got the same problem. I've tried to refresh the web page and to delete the cookies but nothing worked.
P.S : I'm using Safari on OS X 10.9.4.
Thanks in advance
I think you should't hardcode your UUID in model section. Django ORM will use it once while creating database.
In your model you should keep field with UUID, but this UUID should be generated in views.py while uploading files.
So there are steps, how upload should look like:
You feel in form and send it.
Your controller gets the form with data.
In your controller you generate UUID
Your controller saves data in database (and there you should store path to files along with generated UUIDs)
From the upload_to documentation :
This may also be a callable, such as a function, which will be called
to obtain the upload path, including the filename. This callable must
be able to accept two arguments, and return a Unix-style path (with
forward slashes) to be passed along to the storage system. The two
arguments that will be passed are:
What you need to do is create this uuid function and pass it to upload_to.
I found the answer on the mailing list of Django. Here is how you get the value of a field :
def dir_file(analysis, file_name): # should live outside your model
return 'documents/%s/%s' % ( analysis.uuidDir, file_name)
In the model, you put :
uuidDir = models.CharField(max_length = 36)
structureFile = models.FileField(upload_to = dir_file)
Now my directory are UUID and generated at each form submission !

Overwrite over a file and delete old model's object if the file exists

I have made something to overwrite a file already uploaded with :
class OverwriteStorage(FileSystemStorage):
def get_available_name(self, name):
if self.exists(name):
os.remove(os.path.join(settings.MEDIA_ROOT, name))
return name
But my file is in a model :
class Work (models.Model):
file = models.FileField(storage=OverwriteStorage(), upload_to=path)
group = models.ForeignKey(Group, related_name='work_list')
And the new upload makes a new enter, so I have :
A model without file (that bug when I ask file.size..)
My new model
How can I remove my model when my file is deleted?
I have tried to change again FileSystemStorage process but I can't use any argument (said in doc and tested for hours ;)), I have tried to change save process too, but I didn't succeed..
A few things to check before continuing to troubleshoot:
Have you set the MEDIA_ROOT and MEDIA_URL in the settings.py file?
Check this link for more info on Managing files
I would take advantage of Django's built-in file storage rather than building something from scratch
My solution :
for work in groupwork : #It is the list of work associate with my group
try :
path = work.file.path.lstrip(SITE_ROOT+'/'+MEDIA_ROOT+'/').rstrip(request.FILES['file'].name)
deletedwork = groupwork.get(file=path+request.FILES['file'].name)
deletedwork.delete()
except:
pass

replace the file associated with an imagefield without having django copying it

I have a userprofile of the form
class profile():
#the next line is just an abstract
profile_images='volumes/media/root/userprofile/profile_images/'
image=models.ImageField(upload_to=profile_images)
in the directory "profile_images" there are the last 5 files the user uploaded as profile images, ie:
image_1
image_2
image_3
image_4
image_5
lets say the current profile.image is image_1. now i want to allow the user to select one of the previous images. the function i wrote to change the image to the one i received from the form looks like that:
def change_profile_image(userprofile,path_to_new_image):
f = open(path_to_new_image, 'r')
userprofile.image = ImageFile(f)
userprofile.save()
as an example the user selects image_3, and after execution of that code the forementioned directory looks like that:
image_1
image_2
image_3
image_4
image_5
volumes/media/root/userprofile/profile_images/image_3
which, of course, is not what i wanted. what i want is to just change the file associated with the ImageField of my profile instance, without Django copying any files.
any ideas how to solve that?
ok, actually it's as easy as
userprofile.image=path_to_new_image
no need to worry with opening files, deleting and rewriting them.
Theoretically, you could overwrite userprofile.image.path, but it’s not too obvious how to do that.
Here is some more information.
Programmatically saving image to Django ImageField
Django: How to replace/overwrite/update/change a file of FileField?
How can I replace/override an uploaded file?