Collections: use the member stokens for filtering based on stoken
While at it, also generalised the stoken handling to be generic and extendible.
This commit is contained in:
parent
1f18f4e50b
commit
d93a5d3f06
@ -17,6 +17,7 @@ from pathlib import Path
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.validators import RegexValidator
|
from django.core.validators import RegexValidator
|
||||||
|
from django.db.models import Q
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.crypto import get_random_string
|
from django.utils.crypto import get_random_string
|
||||||
|
|
||||||
@ -51,12 +52,14 @@ class Collection(models.Model):
|
|||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def stoken(self):
|
def stoken(self):
|
||||||
last_revision = CollectionItemRevision.objects.filter(item__collection=self).last()
|
stoken = Stoken.objects.filter(
|
||||||
if last_revision is None:
|
Q(collectionitemrevision__item__collection=self) | Q(collectionmember__collection=self)
|
||||||
# FIXME: what is the etag for None? Though if we use the revision for collection it should be shared anyway.
|
).order_by('id').last()
|
||||||
return None
|
|
||||||
|
|
||||||
return last_revision.stoken.uid
|
if stoken is None:
|
||||||
|
raise Exception('stoken is None. Should never happen')
|
||||||
|
|
||||||
|
return stoken.uid
|
||||||
|
|
||||||
|
|
||||||
class CollectionItem(models.Model):
|
class CollectionItem(models.Model):
|
||||||
|
@ -13,12 +13,14 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
from functools import reduce
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.db import transaction, IntegrityError
|
from django.db import transaction, IntegrityError
|
||||||
from django.db.models import Max
|
from django.db.models import Max, Q
|
||||||
|
from django.db.models.functions import Greatest
|
||||||
from django.http import HttpResponseBadRequest, HttpResponse, Http404
|
from django.http import HttpResponseBadRequest, HttpResponse, Http404
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
@ -70,7 +72,7 @@ User = get_user_model()
|
|||||||
class BaseViewSet(viewsets.ModelViewSet):
|
class BaseViewSet(viewsets.ModelViewSet):
|
||||||
authentication_classes = tuple(app_settings.API_AUTHENTICATORS)
|
authentication_classes = tuple(app_settings.API_AUTHENTICATORS)
|
||||||
permission_classes = tuple(app_settings.API_PERMISSIONS)
|
permission_classes = tuple(app_settings.API_PERMISSIONS)
|
||||||
stoken_id_field = None
|
stoken_id_fields = None
|
||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
serializer_class = self.serializer_class
|
serializer_class = self.serializer_class
|
||||||
@ -93,20 +95,19 @@ class BaseViewSet(viewsets.ModelViewSet):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def filter_by_stoken(self, request, queryset):
|
def filter_by_stoken(self, request, queryset):
|
||||||
stoken_id_field = self.stoken_id_field + '__id'
|
|
||||||
|
|
||||||
stoken_rev = self.get_stoken_obj(request)
|
stoken_rev = self.get_stoken_obj(request)
|
||||||
if stoken_rev is not None:
|
if stoken_rev is not None:
|
||||||
filter_by = {stoken_id_field + '__gt': stoken_rev.id}
|
filter_by_map = map(lambda x: Q(**{x + '__gt': stoken_rev.id}), self.stoken_id_fields)
|
||||||
queryset = queryset.filter(**filter_by)
|
filter_by = reduce(lambda x, y: x | y, filter_by_map)
|
||||||
|
queryset = queryset.filter(filter_by).distinct()
|
||||||
|
|
||||||
return queryset, stoken_rev
|
return queryset, stoken_rev
|
||||||
|
|
||||||
def get_queryset_stoken(self, queryset):
|
def get_queryset_stoken(self, queryset):
|
||||||
stoken_id_field = self.stoken_id_field + '__id'
|
aggr_fields = {x: Max(x) for x in self.stoken_id_fields}
|
||||||
|
aggr = queryset.aggregate(**aggr_fields)
|
||||||
new_stoken_id = queryset.aggregate(stoken_id=Max(stoken_id_field))['stoken_id']
|
maxid = max(map(lambda x: x or -1, aggr.values()))
|
||||||
new_stoken = new_stoken_id and Stoken.objects.get(id=new_stoken_id).uid
|
new_stoken = (maxid >= 0) and Stoken.objects.get(id=maxid).uid
|
||||||
|
|
||||||
return queryset, new_stoken
|
return queryset, new_stoken
|
||||||
|
|
||||||
@ -129,7 +130,7 @@ class CollectionViewSet(BaseViewSet):
|
|||||||
queryset = Collection.objects.all()
|
queryset = Collection.objects.all()
|
||||||
serializer_class = CollectionSerializer
|
serializer_class = CollectionSerializer
|
||||||
lookup_field = 'uid'
|
lookup_field = 'uid'
|
||||||
stoken_id_field = 'items__revisions__stoken'
|
stoken_id_fields = ['items__revisions__stoken__id', 'members__stoken__id']
|
||||||
|
|
||||||
def get_queryset(self, queryset=None):
|
def get_queryset(self, queryset=None):
|
||||||
if queryset is None:
|
if queryset is None:
|
||||||
@ -189,7 +190,7 @@ class CollectionItemViewSet(BaseViewSet):
|
|||||||
queryset = CollectionItem.objects.all()
|
queryset = CollectionItem.objects.all()
|
||||||
serializer_class = CollectionItemSerializer
|
serializer_class = CollectionItemSerializer
|
||||||
lookup_field = 'uid'
|
lookup_field = 'uid'
|
||||||
stoken_id_field = 'revisions__stoken'
|
stoken_id_fields = ['revisions__stoken__id']
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
collection_uid = self.kwargs['collection_uid']
|
collection_uid = self.kwargs['collection_uid']
|
||||||
|
Loading…
Reference in New Issue
Block a user