1

Im trying to read files from FileField, put them all to zip and save that zip to another FileField. Im trying to avoid using temp file but it seems that i might have to.

Here is what i got so far:

def generate_codified_batch(modeladmin, request, queryset):
    for batch in queryset:
        pieces = Pieces.objects.filter(batch=batch)
        mem_zip = InMemoryZipFile(file_name=batch.name)
        for piece in pieces:
            in_file = open(piece.file.path, 'rb')
            data = in_file.read()
            mem_zip.append(filename_in_zip=f'/{piece.folder_assigned}    /{piece.period}/{piece.codification}. \
                         {piece.file_name.rsplit(".")[-1]}'
                           , file_contents=data)
            in_file.close()
        files_codified = ContentFile(mem_zip.data)
        Batches.objects.filter(pk=batch.id).update(file_codified=files_codified)

InMemoryZipFile is a class from this packet: https://bitbucket.org/ruamel/std.zipfile/src/faa2c8fc9e0072f57857078059ded42192af5435/init.py?at=default&fileviewer=file-view-default#init.py-57

Important are only two last lines

files_codified = ContentFile(mem_zip.data)
Batches.objects.filter(pk=batch.id).update(file_codified=files_codified)

mem_zip.data is a property of InMemoryZip and returns bytes object (from InMemoryZip class):

self.in_memory_data = StringIO()
@property    
def data    
     return self.in_memory_data.getvalue()

I cannot for the life of me figure out how to read from that bytes object and pass it to FileField.

dxt
  • 122
  • 1
  • 3
  • 13
MarkoBox
  • 95
  • 10
  • what's not working? You get an error? – dirkgroten Jan 24 '19 at 15:07
  • 1
    Instead of `ContentFile` try using `SimpleUploadedFile`: you can create one using `SimpleUploadedFile.from_dict({'content': mem_zip.data, 'filename': batch.name, 'content-type': 'application/zip'})` – dirkgroten Jan 24 '19 at 15:14
  • Thank you, this is useful. Record is in the database now but file is not saved to the directory so trying to download it returns Page not found error. i also had to append .zip to filename. – MarkoBox Jan 24 '19 at 16:08
  • 1
    You shouldn't use `update()` on the Queryset because it doesn't call `save()` on the model, which is what will save the file to disk. Assign the file to the FileField, then save your instance. – dirkgroten Jan 24 '19 at 16:13

1 Answers1

4

To assign an in-memory file to a FileField of a Model, you can should use InMemoryUploadedFile or even easier, its subclass SimpleUploadedFile.

Also you should not use a QuerySet's update() function because that only performs the database query but doesn't call a model's save() method which is what saves the file to disk:

So in your code do this:

files_codified = SimpleUploadedFile.from_dict({
    'content': mem_zip.data, 
    'filename': batch.name + ".zip", 
    'content-type': 'application/zip'})
batch.files_codified = files_codified
batch.save()
dirkgroten
  • 20,112
  • 2
  • 29
  • 42