From 0a3bb6f4bb47b38313b5e4b8ce7ca278eebf5b2f Mon Sep 17 00:00:00 2001 From: Tom Hacohen Date: Thu, 20 Feb 2020 12:02:59 +0200 Subject: [PATCH] Merge item snapshot and item to be one model. --- .../migrations/0002_auto_20200220_0943.py | 53 +++++++++++++++++++ .../migrations/0003_collectionitem_current.py | 18 +++++++ .../migrations/0004_auto_20200220_1029.py | 18 +++++++ django_etesync/models.py | 32 ++++++----- django_etesync/serializers.py | 28 +++++----- django_etesync/views.py | 4 +- 6 files changed, 121 insertions(+), 32 deletions(-) create mode 100644 django_etesync/migrations/0002_auto_20200220_0943.py create mode 100644 django_etesync/migrations/0003_collectionitem_current.py create mode 100644 django_etesync/migrations/0004_auto_20200220_1029.py diff --git a/django_etesync/migrations/0002_auto_20200220_0943.py b/django_etesync/migrations/0002_auto_20200220_0943.py new file mode 100644 index 0000000..c150a11 --- /dev/null +++ b/django_etesync/migrations/0002_auto_20200220_0943.py @@ -0,0 +1,53 @@ +# Generated by Django 3.0.3 on 2020-02-20 09:43 + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_etesync', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='collectionitemchunk', + name='itemSnapshot', + ), + migrations.AddField( + model_name='collectionitem', + name='hmac', + field=models.CharField(default='', max_length=50), + preserve_default=False, + ), + migrations.AddField( + model_name='collectionitemchunk', + name='items', + field=models.ManyToManyField(related_name='chunks', to='django_etesync.CollectionItem'), + ), + migrations.AlterField( + model_name='collection', + name='uid', + field=models.CharField(db_index=True, max_length=44, validators=[django.core.validators.RegexValidator(message='Not a valid UID. Expected a 256bit base64url.', regex='[a-zA-Z0-9\\-_=]{44}')]), + ), + migrations.AlterField( + model_name='collectionitem', + name='collection', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='django_etesync.Collection'), + ), + migrations.AlterField( + model_name='collectionitem', + name='uid', + field=models.CharField(db_index=True, max_length=44, validators=[django.core.validators.RegexValidator(message='Not a valid UID. Expected a 256bit base64url.', regex='[a-zA-Z0-9\\-_=]{44}')]), + ), + migrations.AlterField( + model_name='collectionitemchunk', + name='uid', + field=models.CharField(db_index=True, max_length=44, validators=[django.core.validators.RegexValidator(message='Not a valid UID. Expected a 256bit base64url.', regex='[a-zA-Z0-9\\-_=]{44}')]), + ), + migrations.DeleteModel( + name='CollectionItemSnapshot', + ), + ] diff --git a/django_etesync/migrations/0003_collectionitem_current.py b/django_etesync/migrations/0003_collectionitem_current.py new file mode 100644 index 0000000..2ffbf54 --- /dev/null +++ b/django_etesync/migrations/0003_collectionitem_current.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.3 on 2020-02-20 09:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_etesync', '0002_auto_20200220_0943'), + ] + + operations = [ + migrations.AddField( + model_name='collectionitem', + name='current', + field=models.BooleanField(default=True), + ), + ] diff --git a/django_etesync/migrations/0004_auto_20200220_1029.py b/django_etesync/migrations/0004_auto_20200220_1029.py new file mode 100644 index 0000000..1ea337a --- /dev/null +++ b/django_etesync/migrations/0004_auto_20200220_1029.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.3 on 2020-02-20 10:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_etesync', '0003_collectionitem_current'), + ] + + operations = [ + migrations.AlterField( + model_name='collectionitem', + name='current', + field=models.BooleanField(db_index=True, default=True), + ), + ] diff --git a/django_etesync/models.py b/django_etesync/models.py index 9e578ba..f773495 100644 --- a/django_etesync/models.py +++ b/django_etesync/models.py @@ -12,6 +12,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from pathlib import Path + from django.db import models from django.conf import settings from django.core.validators import RegexValidator @@ -33,45 +35,41 @@ class Collection(models.Model): def __str__(self): return self.uid + @cached_property + def current_items(self): + return self.items.filter(current=True) + class CollectionItem(models.Model): uid = models.CharField(db_index=True, blank=False, null=False, max_length=44, validators=[UidValidator]) version = models.PositiveSmallIntegerField() encryptionKey = models.BinaryField(editable=True, blank=False, null=False) - collection = models.ForeignKey(Collection, on_delete=models.CASCADE) + collection = models.ForeignKey(Collection, related_name='items', on_delete=models.CASCADE) + hmac = models.CharField(max_length=50, blank=False, null=False) + current = models.BooleanField(db_index=True, default=True) class Meta: unique_together = ('uid', 'collection') - @cached_property - def content(self): - return self.snapshots.get(current=True) - def __str__(self): return self.uid -class CollectionItemSnapshot(models.Model): - item = models.ForeignKey(CollectionItem, related_name='snapshots', on_delete=models.CASCADE) - current = models.BooleanField(default=True) - chunkHmac = models.CharField(max_length=50, blank=False, null=False) - - class Meta: - unique_together = ('item', 'current') - - def __str__(self): - return "{}, current={}".format(self.item.uid, self.current) +def chunk_directory_path(instance, filename): + col = instance.itemSnapshot.item.collection + user_id = col.owner.id + return Path('user_{}'.format(user_id), col.uid, instance.uid) class CollectionItemChunk(models.Model): uid = models.CharField(db_index=True, blank=False, null=False, max_length=44, validators=[UidValidator]) - itemSnapshot = models.ForeignKey(CollectionItemSnapshot, related_name='chunks', null=True, on_delete=models.SET_NULL) + items = models.ManyToManyField(CollectionItem, related_name='chunks') order = models.CharField(max_length=100, blank=False, null=False) + # We probably just want to implement this manually because we can have more than one pointing to a file. chunkFile = models.FileField(upload_to=chunk_directory_path) class Meta: - # unique_together = ('itemSnapshot', 'order') # Currently off because we set the item snapshot to null on deletion ordering = ['order'] def __str__(self): diff --git a/django_etesync/serializers.py b/django_etesync/serializers.py index d4f259f..15034c2 100644 --- a/django_etesync/serializers.py +++ b/django_etesync/serializers.py @@ -64,25 +64,27 @@ class CollectionItemChunkSerializer(serializers.ModelSerializer): fields = ('uid', ) -class CollectionItemSnapshotSerializer(serializers.ModelSerializer): +class CollectionItemSerializer(serializers.ModelSerializer): + encryptionKey = BinaryBase64Field() chunks = serializers.SlugRelatedField( slug_field='uid', - queryset=models.CollectionItemChunk, + queryset=models.CollectionItemChunk.objects.all(), many=True ) class Meta: - model = models.CollectionItemSnapshot - fields = ('chunks', 'chunkHmac') + model = models.CollectionItem + fields = ('uid', 'version', 'encryptionKey', 'chunks', 'hmac') -class CollectionItemSerializer(serializers.ModelSerializer): - encryptionKey = BinaryBase64Field() - content = CollectionItemSnapshotSerializer( - read_only=True, - many=False - ) +class CollectionItemInlineSerializer(CollectionItemSerializer): + chunksData = serializers.SerializerMethodField('get_inline_chunks_from_context') - class Meta: - model = models.CollectionItem - fields = ('uid', 'version', 'encryptionKey', 'content') + class Meta(CollectionItemSerializer.Meta): + fields = CollectionItemSerializer.Meta.fields + ('chunksData', ) + + def get_inline_chunks_from_context(self, obj): + request = self.context.get('request', None) + if request is not None: + return ['SomeInlineData', 'Somemoredata'] + return 'readOnly' diff --git a/django_etesync/views.py b/django_etesync/views.py index 4d99dad..8724ae2 100644 --- a/django_etesync/views.py +++ b/django_etesync/views.py @@ -27,11 +27,11 @@ from rest_framework import viewsets from rest_framework.response import Response from . import app_settings, paginators -from .models import Collection, CollectionItem, CollectionItemSnapshot, CollectionItemChunk +from .models import Collection, CollectionItem, CollectionItemChunk from .serializers import ( CollectionSerializer, CollectionItemSerializer, - CollectionItemSnapshotSerializer, + CollectionItemInlineSerializer, CollectionItemChunkSerializer )