Django: Save user uploads in seperate folders

12,819

Solution 1

  1. I don't recommend you to use user email or any other information that can be updated as folder name because you won't change folder name each time he changes his email or his username. So, use user id that is unique and unchangeable.

  2. Here is a complete example from Django documentation, to access instance information in your models to build path with user id :

def user_directory_path(instance, filename):
    # file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
    return 'user_{0}/{1}'.format(instance.user.id, filename)

class MyModel(models.Model):
    upload = models.FileField(upload_to=user_directory_path)

In this case, it use the user id in the folder name. Of course, your can replaceFileField with ImageField.

More information in django docs : FileFields.upload_to

Solution 2

You could maybe separate the folder by their username? You can create a function that would create a folder using the users username like so:

def get_user_image_folder(instance, filename):
    return "%s/%s" %(instance.user.username, filename)

and in your model you could easily use the upload_to and add the function that you just created to it:

class Images(models.Model):
   user = models.ForeignKey(User)
   image = models.ImageField(upload_to=get_user_image_folder,
                                  verbose_name='Image', )

You don't have to use request in Models, you use instance instead.

Solution 3

To get this to work I implemented the solution from the docs as suggested by Louis Barranqueiro, whereby the models looks like:

# models.py
def user_directory_path(instance, filename):
    return 'user_{0}/{1}'.format(instance.user.id, filename)

class Document(models.Model):
    file = models.FileField(upload_to=user_directory_path)
    uploaded_at = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='documents')

But crucially, I also changed my DocumentUploadView class to include a save step, so that the document saves with the user attribute (note also the initial commit=False save step, which is crucial):

# views.py
class DocumentUploadView(View):

    def get(self, request):
        documents_list = Document.objects.all()
        return render(self.request, 'file_upload.html', {'documents': documents_list})

    def post(self, request):
        form = DocumentForm(self.request.POST, self.request.FILES)
        if form.is_valid():
            document = form.save(commit=False)
            document.user = request.user
            document.save()
            data = {'is_valid': True, 'name': document.file.name, 'url': document.file.url}
        else:
            data = {'is_valid': False}
        return JsonResponse(data)

Finally my forms.py looks like this:

# forms.py
class DocumentForm(forms.ModelForm):
    class Meta:
        model = Document
    fields = ('file',)

Solution 4

For anyone else in the future that stumble across this, I got access to the current user's ID by adding the user object to the model in the view.

views.py

from .models import Document

if request.method == 'POST':
    form = DocumentForm(request.POST, request.FILES)
    if form.is_valid():
        newdoc = Document(docfile=request.FILES['docfile'])
        newdoc.owner = request.user
        newdoc.save()

Then, in models.py you can retrieve the ID from owner created previously in view.

def user_directory_path(instance, filename):
    return 'Users/user_{0}/{1}'.format(instance.owner.id, filename)


class Document(models.Model):
    docfile = models.FileField(upload_to=user_directory_path)
Share:
12,819
Kane
Author by

Kane

Updated on July 18, 2022

Comments

  • Kane
    Kane almost 2 years

    I want individual users to be able to upload their files into a single folder (so each user has their own root folder, where they can upload their own files), but I am not sure of the best way to go about implementing this.

    I originally planned to use the users email as the their folder name, and all their uploads would be saved in this folder. However, from what I have gathered the only way of retrieving this information is through the request function, and I cannot manage to get an instance of request into the models.py file, and therefore cannot add it to my 'upload_to' directory.

    Any other ideas of how to separate users files, or how to get an instance of request in the models file would be greatly appreciated!!

    Here is my current model:

    def user_directory_path(instance, filename):
        return 'user_{0}/{1}'.format(instance.user.id, filename)
    
    class UploadModel(models.Model):
        user = models.OneToOneField(User)
        file = models.FileField(upload_to=user_directory_path)
    

    And it's associated error:

    Exception Value: UploadModel has no user.
    
  • Kane
    Kane over 8 years
    1. Ah good point, I'll use user id then. 2. I had come across this but received an error and forgot about it. But now I have applied it again, however I still receive the same error: " 'UploadModel' object has no attribute 'user' " where 'UploadModel' is the model which contains my FileField
  • Louis Barranqueiro
    Louis Barranqueiro over 8 years
    I gave you an example, add all related models if you want that I give you a concrete example. instance represent the current object, if your model is your user model, then use instance.id. instance.user.id is used when the current model have a OneToOneField to the default user model relationship.
  • Kane
    Kane over 8 years
    Okay, would adding " user = models.OneToOneField(User) " be correct? Because this threw an error ('UploadModel' has no user), so I feel like I'm missing something. Sorry, I'm new to Django, so please excuse the novice questions.
  • Louis Barranqueiro
    Louis Barranqueiro over 8 years
    add your model in your question and precise Error please
  • Kane
    Kane over 8 years
    Hi there, I have applied this, but I get the error: "Exception Value: UploadModel has no user", as added to my question.
  • Kane
    Kane over 8 years
    Oh, when I don't use the user variable, I then get the error: "table Core_uploadmodel has no column named user_id", not sure where to go with this.
  • qasimalbaqali
    qasimalbaqali over 8 years
    I updated my answer @Kane, I think you need to add a user field in the UploadModel as a FK. Try that out, and don't forget to migrate to update your databse.
  • Louis Barranqueiro
    Louis Barranqueiro over 8 years
    did you make migrations and migrate?
  • Kane
    Kane over 8 years
    Yes, I did. I have however had success sending an instance of the user from the view to the model.