diff --git a/README.md b/README.md index e8525f1c41aa65fa6f64b780ded4280a4012a706..a565c8354947546fef8580a46dc421bde133241c 100644 --- a/README.md +++ b/README.md @@ -144,12 +144,14 @@ class MyModel(models.Model): ## permissions This allows you to add permissions for AnonymousUser, logged in user, author ... in the url: Currently, there are 3 choices : -* PublicPostPermissions -* PrivateProjectPermissions -* NotificationsPermissions +* ObjectPermission +* AnonymousReadOnly +* InboxPermissions Specific permissin classes can be developed to fit special needs. -PublicPostPermissions gives these permissions: +ObjectPermission give permissions assign in the administration + +AnonymousReadOnly gives these permissions: * Anonymous users: can read all posts * Logged in users: can read all posts + create new posts * Author: can read all posts + create new posts + update their own @@ -157,30 +159,15 @@ PublicPostPermissions gives these permissions: ``` from django.conf.urls import url from djangoldp.views import LDPViewSet -from djangoldp.permissions import PublicPostPermissions +from djangoldp.permissions import AnonymousReadOnly urlpatterns = [ - url(r'^projects/', ProjectViewSet.urls(permission_classes=(PublicPostPermissions,))), + url(r'^projects/', ProjectViewSet.urls(permission_classes=(AnonymousReadOnly,))), url(r'^customers/', LDPViewSet.urls(model=Customer)), ] ``` -PrivateProjectPermissions provides the following -* Anonymous users: no permissions -* Logged in users: can read projects if they're in the team -* Users of group Partners: can see all projects + update all projects - -``` -from django.conf.urls import url -from djangoldp.views import LDPViewSet -from djangoldp.permissions import PrivateProjectPermissions - -urlpatterns = [ - url(r'^projects/', ProjectViewSet.urls(permission_classes=(PrivateProjectPermissions,))), - url(r'^customers/', LDPViewSet.urls(model=Customer)), -] -``` -NotificationsPermissions is used for, well, notifications: +InboxPermissions is used for, well, notifications: * Anonymous users: can create notifications but can't read * Logged in users: can create notifications but can't read * Inbox owners: can read + update all notifications @@ -191,7 +178,7 @@ from djangoldp.views import LDPViewSet from djangoldp.permissions import NotificationsPermissions urlpatterns = [ - url(r'^projects/', ProjectViewSet.urls(permission_classes=(NotificationsPermissions,))), + url(r'^projects/', ProjectViewSet.urls(permission_classes=(InboxPermissions,))), url(r'^customers/', LDPViewSet.urls(model=Customer)), ] ``` diff --git a/djangoldp/factories.py b/djangoldp/factories.py index 172e289b5b49acfaf4a80e711ba249cd77c2d3af..90b8937d8bc9dcbacabf76e5366e3580ebf14469 100644 --- a/djangoldp/factories.py +++ b/djangoldp/factories.py @@ -2,6 +2,7 @@ import factory from django.contrib.auth.models import User from django.db.models.signals import post_save + @factory.django.mute_signals(post_save) class UserFactory(factory.django.DjangoModelFactory): class Meta: diff --git a/djangoldp/models.py b/djangoldp/models.py index 50b31c6c8181e7642d9c5dcc11ac1df41b1490d9..02503fb212486c911a20f89112fa6a62700f11e1 100644 --- a/djangoldp/models.py +++ b/djangoldp/models.py @@ -1,5 +1,11 @@ from django.conf import settings from django.db import models +from rest_framework import fields + +class LDPUrlField (fields.URLField): + def to_representation(self, value): + str = super(LDPUrlField, self).to_representation(value) + return {'@id': str} class LDPSource(models.Model): container = models.URLField() diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py index 453363132a9d8d9e7f7b7452fbd4b36f0300d6f5..41bb34d43eb2b69c119e009e70ab1f7ca870cb1f 100644 --- a/djangoldp/serializers.py +++ b/djangoldp/serializers.py @@ -1,42 +1,63 @@ -import json from django.core.exceptions import ImproperlyConfigured from django.core.urlresolvers import get_resolver from django.utils.datastructures import MultiValueDictKeyError +from guardian.shortcuts import get_perms +from rest_framework.fields import empty from rest_framework.relations import HyperlinkedRelatedField, ManyRelatedField, MANY_RELATION_KWARGS -from rest_framework.serializers import HyperlinkedModelSerializer, ListSerializer -from rest_framework.utils.serializer_helpers import ReturnDict +from rest_framework.serializers import HyperlinkedModelSerializer, ListSerializer, ModelSerializer from rest_framework.utils.field_mapping import get_nested_relation_kwargs -from guardian.shortcuts import get_perms +from rest_framework.utils.serializer_helpers import ReturnDict + +from django.db import models as django_models +from djangoldp import models class LDListMixin: def to_internal_value(self, data): - data = json.loads(data) + # data = json.loads(data) + try: + data = data['ldp:contains'] + except TypeError: + pass if isinstance(data, dict): data = [data] - return [self.child_relation.to_internal_value(item['@id']) for item in data] + return [self.child.to_internal_value(item) for item in data] + def to_representation(self, value): - return {'@id': self.id, 'ldp:contains': super().to_representation(value)} + return {'@id': self.id, '@type': 'ldp:Container', 'ldp:contains': super().to_representation(value)} def get_attribute(self, instance): parent_id_field = self.parent.fields[self.parent.url_field_name] context = self.parent.context parent_id = parent_id_field.get_url(instance, parent_id_field.view_name, context['request'], context['format']) - self.id = parent_id + self.field_name+"/" + self.id = parent_id + self.field_name + "/" return super().get_attribute(instance) + class ContainerSerializer(LDListMixin, ListSerializer): - id='' + id = '' + @property def data(self): return ReturnDict(super(ListSerializer, self).data, serializer=self) + def create(self, validated_data): + return super().create(validated_data) + + def to_internal_value(self, data): + try: + return super().to_internal_value(data['@id']) + except: + return super().to_internal_value(data) + + class ManyJsonLdRelatedField(LDListMixin, ManyRelatedField): pass + class JsonLdField(HyperlinkedRelatedField): def __init__(self, view_name=None, **kwargs): super().__init__(view_name, **kwargs) self.get_lookup_args() - + def get_lookup_args(self): try: lookup_field = get_resolver().reverse_dict[self.view_name][0][0][1][0] @@ -45,19 +66,20 @@ class JsonLdField(HyperlinkedRelatedField): except MultiValueDictKeyError: pass + class JsonLdRelatedField(JsonLdField): def to_representation(self, value): try: return {'@id': super().to_representation(value)} except ImproperlyConfigured: return value.pk - + def to_internal_value(self, data): try: return super().to_internal_value(data['@id']) except: return super().to_internal_value(data) - + @classmethod def many_init(cls, *args, **kwargs): list_kwargs = {'child_relation': cls(*args, **kwargs)} @@ -66,36 +88,55 @@ class JsonLdRelatedField(JsonLdField): list_kwargs[key] = kwargs[key] return ManyJsonLdRelatedField(**list_kwargs) + class JsonLdIdentityField(JsonLdField): def __init__(self, view_name=None, **kwargs): kwargs['read_only'] = True kwargs['source'] = '*' super().__init__(view_name, **kwargs) - + def use_pk_only_optimization(self): return False + def to_internal_value(self, data): + try: + return super().to_internal_value(data['@id']) + except: + return super().to_internal_value(data) + + class LDPSerializer(HyperlinkedModelSerializer): url_field_name = "@id" serializer_related_field = JsonLdRelatedField serializer_url_field = JsonLdIdentityField - + + ModelSerializer.serializer_field_mapping [django_models.URLField] = models.LDPUrlField + def get_default_field_names(self, declared_fields, model_info): try: fields = list(self.Meta.model._meta.serializer_fields) - except: + except AttributeError: fields = super().get_default_field_names(declared_fields, model_info) + try: + fields.remove(self.Meta.model._meta.auto_author) + except ValueError: + pass + except AttributeError: + pass return fields + list(getattr(self.Meta, 'extra_fields', [])) - + def to_representation(self, obj): data = super().to_representation(obj) + if hasattr(obj._meta, 'rdf_type'): data['@type'] = obj._meta.rdf_type - data['permissions'] = [{'mode': {'@type': name.split('_')[0]}} for name in get_perms(self.context['request'].user, obj)] + data['permissions'] = [{'mode': {'@type': name.split('_')[0]}} for name in + get_perms(self.context['request'].user, obj)] return data - + def build_nested_field(self, field_name, relation_info, nested_depth): class NestedLDPSerializer(self.__class__): + class Meta: model = relation_info.related_model depth = nested_depth - 1 @@ -104,10 +145,32 @@ class LDPSerializer(HyperlinkedModelSerializer): except AttributeError: fields = '__all__' - - return NestedLDPSerializer, get_nested_relation_kwargs(relation_info) - + def to_internal_value(self, data): + model = self.Meta.model + return self.serializer_related_field( + view_name='{}-detail'.format(model._meta.object_name.lower()), + queryset=model.objects.all()).to_internal_value(data) + + kwargs = get_nested_relation_kwargs(relation_info) + kwargs['read_only'] = False + kwargs['required'] = False + return NestedLDPSerializer, kwargs + @classmethod def many_init(cls, *args, **kwargs): kwargs['child'] = cls() return ContainerSerializer(*args, **kwargs) + + def create(self, validated_data): + nested_fields = [] + nested_fields_name = list(filter(lambda key: isinstance(validated_data[key], list), validated_data)) + for field_name in nested_fields_name: + nested_fields.append((field_name, validated_data.pop(field_name))) + + obj = self.Meta.model.objects.create(**validated_data) + + for (field_name, data) in nested_fields: + for item in data: + getattr(obj, field_name).add(item) + + return obj diff --git a/djangoldp/tests.py b/djangoldp/tests.py deleted file mode 100644 index 7ce503c2dd97ba78597f6ff6e4393132753573f6..0000000000000000000000000000000000000000 --- a/djangoldp/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/djangoldp/tests/models.py b/djangoldp/tests/models.py new file mode 100644 index 0000000000000000000000000000000000000000..d8e0e76be8cc145c7dcc45635a4a342037dcc9a9 --- /dev/null +++ b/djangoldp/tests/models.py @@ -0,0 +1,12 @@ +from django.conf import settings +from django.db import models + + +class Skill(models.Model): + title = models.CharField(max_length=255, blank=True, null=True) + + +class JobOffer(models.Model): + title = models.CharField(max_length=255, blank=True, null=True) + skills = models.ManyToManyField(Skill, blank=True) + diff --git a/djangoldp/tests/runner.py b/djangoldp/tests/runner.py new file mode 100644 index 0000000000000000000000000000000000000000..8e04314473e2e7b8898ed9f3fa004daa303dc121 --- /dev/null +++ b/djangoldp/tests/runner.py @@ -0,0 +1,29 @@ +import django +import sys +from django.conf import settings + +settings.configure(DEBUG=True, + DATABASES={ + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + } + }, + ROOT_URLCONF='djangoldp.tests.urls', + INSTALLED_APPS=('django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.admin', + 'guardian', + 'djangoldp', + 'djangoldp.tests', + )) + + +django.setup() +from django.test.runner import DiscoverRunner + +test_runner = DiscoverRunner(verbosity=1) + +failures = test_runner.run_tests(['djangoldp.tests.tests']) +if failures: + sys.exit(failures) diff --git a/djangoldp/tests/tests.py b/djangoldp/tests/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..fe5de882868459b37beb4d3de5d5b0d0e8a15f6d --- /dev/null +++ b/djangoldp/tests/tests.py @@ -0,0 +1,122 @@ +from django.test import TestCase + +from djangoldp.serializers import LDPSerializer +from djangoldp.tests.models import Skill, JobOffer + + +class Serializer(TestCase): + + def test_container_serializer_save(self): + skill1 = Skill.objects.create(title="skill1") + skill2 = Skill.objects.create(title="skill2") + job = {"title": "job test", + "skills": { + "ldp:contains": [ + {"@id": "https://happy-dev.fr/skills/{}/".format(skill1.pk)}, + {"@id": "https://happy-dev.fr/skills/{}/".format(skill2.pk)}, + ]} + } + + meta_args = {'model': JobOffer, 'depth': 1, 'fields': ("@id", "title", "skills")} + + meta_class = type('Meta', (), meta_args) + serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class}) + serializer = serializer_class(data=job) + serializer.is_valid() + result = serializer.save() + + self.assertEquals(result.title, "job test") + self.assertIs(result.skills.count(), 2) + + def test_save_without_nested_fields(self): + skill1 = Skill.objects.create(title="skill1") + skill2 = Skill.objects.create(title="skill2") + job = {"title": "job test"} + + meta_args = {'model': JobOffer, 'depth': 1, 'fields': ("@id", "title", "skills")} + + meta_class = type('Meta', (), meta_args) + serializer_class = type(LDPSerializer)('JobOfferSerializer', (LDPSerializer,), {'Meta': meta_class}) + serializer = serializer_class(data=job) + serializer.is_valid() + result = serializer.save() + + self.assertEquals(result.title, "job test") + self.assertIs(result.skills.count(), 0) + +from django.test import TestCase, Client, RequestFactory +from djangoldp.views import LDPViewSet +from djangoldp.permissions import AnonymousReadOnly + +from django.contrib.auth.models import AnonymousUser, User +from djangoldp_joboffer.models import JobOffer + + +class TestUserPermissions (TestCase): + def setUp(self): + self.factory = RequestFactory() +# self.c = Client() + self.user = User.objects.create_user(username='john', email='jlennon@beatles.com', password='glass onion') + + def tearDown(self): + self.user.delete() + + def test_get_with_user(self): + request = self.factory.get('/job-offers/') + request.user = self.user + my_view = LDPViewSet.as_view({'get': 'list'}, model=JobOffer, nested_fields=["skills"], permission_classes=[AnonymousReadOnly]) + response = my_view(request) + self.assertEqual(response.status_code, 200) + + def test_request_options_create_with_user(self): + request = self.factory.options('/job-offers/') + request.user = self.user + my_view = LDPViewSet.as_view({'options': 'create'}, model=JobOffer, nested_fields=["skills"], permission_classes=[AnonymousReadOnly]) + response = my_view(request) + self.assertEqual(response.status_code, 201) + + def test_request_options_update_with_user(self): + request = self.factory.options('/job-offers/') + request.user = self.user + my_view = LDPViewSet.as_view({'options': 'update'}, model=JobOffer, nested_fields=["skills"], permission_classes=[AnonymousReadOnly]) + response = my_view(request) + self.assertEqual(response.status_code, 201) + + +class TestAnonymousUserPermissions (TestCase): + def setUp(self): + self.factory = RequestFactory() +# self.c = Client() + self.user = AnonymousUser + + def test_get_request_with_anonymousUser(self): + request = self.factory.get("/job-offers/") + request.user = self.user + my_view = LDPViewSet.as_view({'get': 'list'}, + model=JobOffer, + nested_fields=["skills"], + permission_classes=[AnonymousReadOnly]) + response = my_view(request) + self.assertEqual(response.status_code, 200) + + def test_request_options_create_with_anonymousUser(self): + request = self.factory.options("/job-offers/") + request.user = self.user + my_view = LDPViewSet.as_view({'options': 'create'}, + model=JobOffer, + nested_fields=["skills"], + permission_classes=[AnonymousReadOnly]) + response = my_view(request) + self.assertEqual(response.status_code, 403) + + def test_request_options_update_with_anonymousUser(self): + request = self.factory.options("/job-offers/") + request.user = self.user + my_view = LDPViewSet.as_view({'options': 'update'}, + model=JobOffer, + nested_fields=["skills"], + permission_classes=[AnonymousReadOnly]) + response = my_view(request) + self.assertEqual(response.status_code, 403) + + diff --git a/djangoldp/tests/urls.py b/djangoldp/tests/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..5982784daeaeaf8c7b1f8e59f784632b7ae95cbe --- /dev/null +++ b/djangoldp/tests/urls.py @@ -0,0 +1,9 @@ +from djangoldp.tests.models import Skill, JobOffer +from djangoldp.views import LDPViewSet +from django.conf.urls import url + + +urlpatterns = [ + url(r'^skills/', LDPViewSet.urls(model=Skill, permission_classes=[], fields=["@id", "title"], nested_fields=[])), + url(r'^job-offers/', LDPViewSet.urls(model=JobOffer, nested_fields=["skills"], permission_classes=())), +] \ No newline at end of file diff --git a/djangoldp/views.py b/djangoldp/views.py index 26a84d1ea3dfaa29069bc94134c9e5fdee8c115b..9f38b07954063deaab01ec59ac33e37f3c306a17 100644 --- a/djangoldp/views.py +++ b/djangoldp/views.py @@ -1,4 +1,3 @@ -from pyld import jsonld from django.apps import apps from django.conf import settings from django.conf.urls import url, include @@ -7,33 +6,38 @@ from django.core.urlresolvers import get_resolver from django.db.utils import OperationalError from django.shortcuts import get_object_or_404 from django.utils.decorators import classonlymethod +from guardian.shortcuts import get_objects_for_user +from pyld import jsonld from rest_framework.authentication import SessionAuthentication -from rest_framework.filters import BaseFilterBackend -from rest_framework.renderers import JSONRenderer from rest_framework.parsers import JSONParser -from rest_framework.permissions import DjangoObjectPermissions +from rest_framework.renderers import JSONRenderer from rest_framework.viewsets import ModelViewSet + from .models import LDPSource from .serializers import LDPSerializer -from guardian.shortcuts import get_objects_for_user class JSONLDRenderer(JSONRenderer): media_type = 'application/ld+json' + def render(self, data, accepted_media_type=None, renderer_context=None): data["@context"] = settings.LDP_RDF_CONTEXT return super(JSONLDRenderer, self).render(data, accepted_media_type, renderer_context) + class JSONLDParser(JSONParser): media_type = 'application/ld+json' + def parse(self, stream, media_type=None, parser_context=None): data = super(JSONLDParser, self).parse(stream, media_type, parser_context) return jsonld.compact(data, ctx=settings.LDP_RDF_CONTEXT) + class NoCSRFAuthentication(SessionAuthentication): def enforce_csrf(self, request): return + class LDPViewSetGenerator(ModelViewSet): """An extension of ModelViewSet that generates automatically URLs for the model""" model = None @@ -41,7 +45,7 @@ class LDPViewSetGenerator(ModelViewSet): model_prefix = None list_actions = {'get': 'list', 'post': 'create'} detail_actions = {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'} - + @classonlymethod def get_model(cls, **kwargs): '''gets the model in the arguments or in the viewset definition''' @@ -49,18 +53,18 @@ class LDPViewSetGenerator(ModelViewSet): if isinstance(model, str): model = apps.get_model(model) return model - + @classonlymethod def get_lookup_arg(cls, **kwargs): return kwargs.get('lookup_url_kwarg') or cls.lookup_url_kwarg or kwargs.get('lookup_field') or cls.lookup_field - + @classonlymethod def get_detail_expr(cls, lookup_field=None, **kwargs): '''builds the detail url based on the lookup_field''' lookup_field = lookup_field or cls.get_lookup_arg(**kwargs) lookup_group = r'\d' if lookup_field == 'pk' else r'[\w\-\.]' return r'(?P<{}>{}+)/'.format(lookup_field, lookup_group) - + @classonlymethod def urls(cls, **kwargs): kwargs['model'] = cls.get_model(**kwargs) @@ -68,23 +72,26 @@ class LDPViewSetGenerator(ModelViewSet): if kwargs.get('model_prefix'): model_name = '{}-{}'.format(kwargs['model_prefix'], model_name) detail_expr = cls.get_detail_expr(**kwargs) - + urls = [ - url('^$', cls.as_view(cls.list_actions, **kwargs), name='{}-list'.format(model_name)), - url('^'+detail_expr+'$', cls.as_view(cls.detail_actions, **kwargs), name='{}-detail'.format(model_name)), - ] - + url('^$', cls.as_view(cls.list_actions, **kwargs), name='{}-list'.format(model_name)), + url('^' + detail_expr + '$', cls.as_view(cls.detail_actions, **kwargs), + name='{}-detail'.format(model_name)), + ] + for field in kwargs.get('nested_fields') or cls.nested_fields: - urls.append(url('^'+detail_expr+field+'/', LDPNestedViewSet.nested_urls(field, **kwargs))) - + urls.append(url('^' + detail_expr + field + '/', LDPNestedViewSet.nested_urls(field, **kwargs))) + return include(urls) + class LDPViewSet(LDPViewSetGenerator): """An automatically generated viewset that serves models following the Linked Data Platform convention""" fields = None exclude = None - renderer_classes = (JSONLDRenderer, ) - parser_classes = (JSONLDParser, ) + depth = 0 + renderer_classes = (JSONLDRenderer,) + parser_classes =(JSONLDParser,) authentication_classes = (NoCSRFAuthentication,) def __init__(self, **kwargs): @@ -95,32 +102,32 @@ class LDPViewSet(LDPViewSetGenerator): self.filter_backends = p.filter_class self.serializer_class = self.build_serializer() - - + def build_serializer(self): model_name = self.model._meta.object_name.lower() - lookup_field = get_resolver().reverse_dict[model_name+'-detail'][0][0][1][0] - meta_args = {'model': self.model, 'extra_kwargs': {'@id': {'lookup_field': lookup_field}}, 'depth': 2, 'extra_fields': self.nested_fields} + lookup_field = get_resolver().reverse_dict[model_name + '-detail'][0][0][1][0] + meta_args = {'model': self.model, 'extra_kwargs': {'@id': {'lookup_field': lookup_field}}, 'depth': self.depth, + 'extra_fields': self.nested_fields} if self.fields: meta_args['fields'] = self.fields else: meta_args['exclude'] = self.exclude or () meta_class = type('Meta', (), meta_args) - return type(LDPSerializer)(model_name+'Serializer', (LDPSerializer,), {'Meta': meta_class}) - + return type(LDPSerializer)(model_name + 'Serializer', (LDPSerializer,), {'Meta': meta_class}) + def perform_create(self, serializer, **kwargs): if hasattr(self.model._meta, 'auto_author'): kwargs[self.model._meta.auto_author] = self.request.user serializer.save(**kwargs) - + def get_queryset(self, *args, **kwargs): if self.model: return self.model.objects.all() - perm="view_{}".format(self.model._meta.model_name.lower()) - return get_objects_for_user(self.request.user, perm, klass=self.model) + perm = "view_{}".format(self.model._meta.model_name.lower()) + return get_objects_for_user(self.request.user, perm, klass=self.model) else: return super(LDPView, self).get_queryset(*args, **kwargs) - + def dispatch(self, request, *args, **kwargs): response = super(LDPViewSet, self).dispatch(request, *args, **kwargs) response["Access-Control-Allow-Origin"] = request.META.get('HTTP_ORIGIN') @@ -130,6 +137,10 @@ class LDPViewSet(LDPViewSetGenerator): response["Accept-Post"] = "application/ld+json" return response + def update(self, request, *args, **kwargs): + response = super().update(request, *args, **kwargs) + return response + class LDPNestedViewSet(LDPViewSet): """A special case of LDPViewSet serving objects of a relation of a given object (e.g. members of a group, or skills of a user)""" parent_model = None @@ -137,24 +148,24 @@ class LDPNestedViewSet(LDPViewSet): related_field = None nested_field = None nested_related_name = None - + def get_parent(self): return get_object_or_404(self.parent_model, **{self.parent_lookup_field: self.kwargs[self.parent_lookup_field]}) - + def perform_create(self, serializer, **kwargs): kwargs[self.nested_related_name] = self.get_parent() super().perform_create(serializer, **kwargs) - + def get_queryset(self, *args, **kwargs): if self.related_field.many_to_many or self.related_field.one_to_many: return getattr(self.get_parent(), self.nested_field).all() if self.related_field.many_to_one or self.related_field.one_to_one: return [getattr(self.get_parent(), self.nested_field)] - + @classonlymethod def get_related_fields(cls, model): - return {field.get_accessor_name():field for field in model._meta.fields_map.values()} - + return {field.get_accessor_name(): field for field in model._meta.fields_map.values()} + @classonlymethod def nested_urls(cls, nested_field, **kwargs): try: @@ -165,30 +176,31 @@ class LDPNestedViewSet(LDPViewSet): nested_related_name = related_field.related_query_name() else: nested_related_name = related_field.remote_field.name - + return cls.urls( - model = related_field.related_model, - exclude = (nested_related_name,) if related_field.one_to_many else (), - parent_model = cls.get_model(**kwargs), - nested_field = nested_field, - nested_related_name = nested_related_name, - related_field = related_field, - parent_lookup_field = cls.get_lookup_arg(**kwargs), - model_prefix = cls.get_model(**kwargs)._meta.object_name.lower(), - permission_classes = kwargs.get('permission_classes', ()), - lookup_url_kwarg = related_field.related_model._meta.object_name.lower()+'_id') + model=related_field.related_model, + exclude=(nested_related_name,) if related_field.one_to_many else (), + parent_model=cls.get_model(**kwargs), + nested_field=nested_field, + nested_related_name=nested_related_name, + related_field=related_field, + parent_lookup_field=cls.get_lookup_arg(**kwargs), + model_prefix=cls.get_model(**kwargs)._meta.object_name.lower(), + permission_classes=kwargs.get('permission_classes', ()), + lookup_url_kwarg=related_field.related_model._meta.object_name.lower() + '_id') + class LDPSourceViewSet(LDPViewSet): model = LDPSource federation = None - + @classonlymethod def urls(cls, **kwargs): try: - return include([url(name+'/', super(LDPSourceViewSet, cls).urls(federation=name, **kwargs)) - for name in LDPSource.objects.order_by().values_list('federation', flat=True).distinct()]) - except OperationalError: #for the case where the table doesn't exist + return include([url(name + '/', super(LDPSourceViewSet, cls).urls(federation=name, **kwargs)) + for name in LDPSource.objects.order_by().values_list('federation', flat=True).distinct()]) + except OperationalError: # for the case where the table doesn't exist return include([]) - + def get_queryset(self, *args, **kwargs): return super().get_queryset(*args, **kwargs).filter(federation=self.federation)