0

I am trying to upload files and organise the directory structure in the media folder. In specific I want the upload to create subfolders basis one of the values in the model. The issue I face is that in the view I add information to the instance (in my example code this is the relevant profile). I would like to use this information for the subfolder, but it does not exist in my model until the save which is after the upload...

What would be the appropriate approach to get the information into Upload so that the subfolder can be created?

Thanks

Model:

class Upload(models.Model):
    file = models.FileField(upload_to="upload/")
    profile = models.ForeignKey(Profile, blank=True, null=True)

    def get_upload_to(self, field_attname):
        return 'upload/%d' % self.profile

View:

def profile(request, profile_slug):
    profile = Profile.objects.get(slug=profile_slug)
    context_dict['profile'] = profile
    if request.method=="POST":
            for file in request.FILES.getlist('file'):
                    upload = UploadForm(request.POST, request.FILES)
                    if upload.is_valid():
                            newupload = upload.save(commit=False)
                            newupload.profile = profile
                            newupload.save()
    else:
        pass

    upload=UploadForm()
    context_dict['form'] = upload

    return render(request, 'app/profile.html', context_dict)

SOLUTION, thanks to xyres:

Model:

def get_upload_to(instance, filename):
    return 'upload/%s/%s' % (instance.profile, filename)

class Upload(models.Model):
    file = models.FileField(upload_to=get_upload_to)
    profile = models.ForeignKey(Profile, blank=True, null=True)

View:

def profile(request, profile_slug):
    profile = Profile.objects.get(slug=profile_slug)
    context_dict['profile'] = profile
    if request.method=="POST":
        upload = UploadForm(request.POST, request.FILES)
        if upload.is_valid():
            for f in request.FILES.getlist('file'):
                Upload.objects.create(file=f, profile=profile)
        return redirect(reverse('profile')
    else:
        pass

    return render(request, 'app/profile.html', context_dict)
marckr
  • 59
  • 1
  • 9

1 Answers1

5

The function which will return the upload path takes 2 arguments, namely: instance and filename. You'll need to define this function outside this class and supply this function to upload_to option.

This is how you need to rewrite your code:

def get_upload_to(instance, filename):
    return 'upload/%d/%s' % (instance.profile, filename)


class Upload(models.Model):
    file = models.FileField(upload_to=get_upload_to)
    profile = models.ForeignKey(Profile, blank=True, null=True)

EDIT:

In case you're wondering why get_upload_to can't be defined inside the class, below is the error you'll most likely get:

ValueError: Could not find function get_upload_to in categ.models.

Please note that due to Python 2 limitations, you cannot serialize unbound method functions (e.g. a method declared and used in the same class body). Please move the function into the main module body to use migrations.

xyres
  • 20,487
  • 3
  • 56
  • 85
  • So not yet perfect.. The directory structure is working well, however I just only noticed that the file is the same for every uploaded file. It will save the first file in the upload as often as there are number of files. It seems "for file in request.FILES.getlist('file'):" is just repeated for first file only... – marckr Feb 29 '16 at 16:29
  • 1
    @marckr I can't really say what is causing that behaviour. But looking at [this answer](http://stackoverflow.com/a/20163041/1925257) and [this answer](http://stackoverflow.com/a/856126/1925257), I think it should work as expected. – xyres Feb 29 '16 at 17:40
  • Thanks, I'll open a new question for that. – marckr Mar 01 '16 at 10:57