Search code examples
djangofile-uploadfilefielddjango-file-upload

Copying files from a saved FileField to an UploadedFile in Django


I need to save files, not from request.FILES, but from another saved record.

Here's the code for the model record:

class Foo(models.Model)
    slug = models.SlugField()

class FooFile(models.Model):
    name = models.CharField(max_length=100)
    file = models.FileField(upload_to='foo_folder')
    foo = models.ForeignKey(Foo, related_name='files')

class RealRecord(models.Model):
    slug = models.SlugField()
    awesome_file=models.FileField(upload_to='awesome')
    mediocre_file=models.FileField(upload_to='mediocre')

And the view (in this case MyForm is a model form that saves to RealRecord):

def example(request, record=1, template_name="form.html")
    foo_obj = Foo.objects.get(pk=record)
    SAVED_FILES = {}
    for file in foo_obj.files.all():
         SAVED_FILES[file.name]=file.file
    if request.method == 'POST':
        form = MyForm(data=request.POST, files=SAVED_FILES)
        if form.is_valid():
            form.save() 
            # rest of view
    else:
        form = MyForm()
    return render(request, template_name, locals())

So the thing is basically a FieldFile is being used as an UploadedFile object.

Each Foo will have a FooFile record with the name awesome_file and another with the name mediocre_file, matching up with the required fields in RealRecord.

The crazy thing is, this totally validates. However, the problem is that in the resulting record that is created, both awesome_file and mediocre_file have their path in "foo_folder". But I don't want the files in "foo_folder", I want them to be in the path that I specified for each field in RealRecord.

So I guess I am wondering what I can do to the FieldFile values coming from FooField so that they behave like a traditional UploadedFile and get the upload_to and path values of their respective fields.


Solution

  • Awww... you guys! I was really hoping someone would come up with an answer. Anyway, I was able to come up with my own solution; not sure if it's the optimal one but it works.

    I made a slight mod to FooFile so it also stores the content_type of the uploaded file:

    class FooFile(models.Model):
        name = models.CharField(max_length=100)
        file = models.FileField(upload_to='foo_folder')
        content_type = models.CharField(max_length=254) # max length given by RFC 4288 
        foo = models.ForeignKey(Foo, related_name='files')
    

    and then, in the view, I create a SimpleUploadedFile object for each FooFile record:

    from django.core.files.uploadedfile import SimpleUploadedFile
    import os
    
    def example(request, record=1, template_name="form.html")
        foo_obj = Foo.objects.get(pk=record)
        SAVED_FILES = {}
        for saved_file in foo_obj.files.all():
            SAVED_FILES[file.name]=SimpleUploadedFile(os.path.basename(saved_file.file.path), saved_file.file.read(), saved_file.content_type)
        if request.method == 'POST':
            form = MyForm(data=request.POST, files=SAVED_FILES)
            if form.is_valid():
                form.save() 
                # rest of view
        else:
            form = MyForm()
        return render(request, template_name, locals())