Skip to content

Instantly share code, notes, and snippets.

@JordanReiter
Last active March 27, 2019 15:17
Show Gist options
  • Save JordanReiter/29619a19c1075c91f82ed16927ab7985 to your computer and use it in GitHub Desktop.
Save JordanReiter/29619a19c1075c91f82ed16927ab7985 to your computer and use it in GitHub Desktop.
File field for files stored in S3 that provides easy access to previous versions
'''
Adds access to previous versions to a model's file field.
Requires django-storages, and you must either be using S3Boto3Storage
by default, or you must specify it as the storage class in the field
definition.
class SampleModel(models.Model):
file = S3FileField(upload_to='path/to/dir')
>> record = SampleModel.objects.all()[0]
>> record.file
<S3FieldFile: path/to/dir/filename.docx>
>> record.file.versions
<S3FileVersions: {1: <Key: bucket_name,path/to/dir/filename.docx>, 2: <Key: bucket_name,path/to/dir/filename.docx>,
3: <Key: bucket_name,path/to/dir/filename.docx>}>
>> record.file.versions.first
<Key: bucket_name,path/to/dir/filename.docx>
>> record.file.versions.first.version
1
>> record.file.versions.first.date
datetime.datetime(2019, 1, 1, 8, 25, 45)
>> record.file.versions.first.url
https://s3.amazonaws.com:443/bucket_name/path/to/dir/filename.docx?Signature=...&Expires=...&AWSAccessKeyId=...&versionId=...
>> record.file.versions[2].date
datetime.datetime(2019, 1, 3, 11, 8, 11)
>> record.file.versions[2].version
2
>> record.file.versions.previous.version
2
>> record.file.versions.current.version
3
'''
class S3FileVersions:
def __init__(self, current, versions, expires_in=None):
expires_in = expires_in or 100
self.versions = {}
for vv_idx, vv in enumerate(sorted(versions, key=lambda x: x.last_modified)):
setattr(vv, 'date', datetime.datetime.strptime(vv.last_modified, '%Y-%m-%dT%H:%M:%S.%fZ'))
setattr(vv, 'version', vv_idx + 1)
setattr(vv, 'url', vv.generate_url(expires_in))
self.versions[vv_idx + 1] = vv
if vv.version_id == current.version_id:
current = vv
self.current = current
def __iter__(self):
return iter(self.versions.values())
@property
def previous(self):
return self.versions[max(
kk for kk, vv in
self.versions.items()
if vv.version_id != self.current.version_id
)]
@property
def first(self):
return self.versions[min(self.versions.keys())]
def __getitem__(self, version):
return self.versions[version]
def __repr__(self):
return '<S3FileVersions: {}>'.format(repr(self.versions))
class S3FieldFile(FieldFile):
@property
def versions(self):
current = self.storage.bucket.get_key(self.name)
return S3FileVersions(current, self.storage.bucket.list_versions(self.name))
class S3FileField(models.FileField):
attr_class = S3FieldFile
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment