How to PUT a file in Django

Once we decide to go for PUT instead of POST, we step out the comfort zone of django, there is no mapped form filed, no validation, we have to deal with the raw WSGI interface by ourselves. Anyway, we can still use the django.core.file.File class.

If we dig into the source code, the django.core.file.File defines: open, close, read, tell, seek, flushand some other django-specific operations, like chunks, readlines, xreadlines etc. Ticket #8501 glues File and file object when chunks method is missing.

It is interesting that the interface File exposed explicitly requires that the underlying file object supports random access, which is most likely overkill for regular usage. Sometimes, less is more. And it implicitly expects read will return EOF, which is also not true for WSGI.input. So we end up to brew our own:

class SocketFile(File):
    # Only forward access is allowed
    def __init__(self, socket, size):
        super(SocketFile, self).__init__(socket)
        self._size = int(size)
        self._pos = 0

    def read(self, num_bytes=None):
        if num_bytes is None:
            num_bytes = self._size - self._pos
        else:
            num_bytes = min(num_bytes, self._size - self._pos)
        self._pos += num_bytes
        return self.file.read(num_bytes)

     def tell(self):
         return self._pos

     def seek(self, position):
         pass

The SocketFile object is initialized with the length of the socket file object, aka CONTENT_LENGTH, the read method gatekeeps the operation to return EOF. seek is inherited from File, so just bypass it. Just wrap the raw WSGI.input with SocketFile, and use it as File. Please check views.py for the usage.