diff --git a/api/event/serializers.py b/api/event/serializers.py index 01adbc3..8abc556 100644 --- a/api/event/serializers.py +++ b/api/event/serializers.py @@ -9,14 +9,29 @@ from api.users.serializers import UserSerializer User = get_user_model() +# Event Serializer +class EventSerializer(serializers.HyperlinkedModelSerializer): + created_by = serializers.ReadOnlyField(source='created_by.get_full_name') + creation_date = serializers.ReadOnlyField() + + class Meta: + model = Event + fields = ('url', 'id', 'title', 'slug', 'created_by', 'creation_date', + 'description', 'beginning_date', 'ending_date') + + # Classes utilitaires class EventSpecificSerializer(serializers.ModelSerializer): """ Provide `update` and `create` methods for nested view with an Event - For example for Models which exetends EventSpecificMixin + For example for Models which extends EventSpecificMixin the event id has to be provided in the `save` method Works fine with view.EventSpecificViewSet + Also provides : + event = eventserializer(allow_null=true, read_only=true) """ + event = EventSerializer(allow_null=True, read_only=True) + def update(self, instance, validated_data): """ Note : does NOT change the event value of the instance @@ -36,19 +51,8 @@ class EventSpecificSerializer(serializers.ModelSerializer): # Serializers -class EventSerializer(serializers.HyperlinkedModelSerializer): - created_by = serializers.ReadOnlyField(source='created_by.username') - - class Meta: - model = Event - fields = ('url', 'id', 'title', 'slug', 'created_by', 'creation_date', - 'description', 'beginning_date', 'ending_date') - - # TODO rajouter des permissions class PlaceSerializer(EventSpecificSerializer): - event = EventSerializer(allow_null=True, read_only=True) - class Meta: model = Place fields = ('url', 'id', 'name', 'description', 'event') @@ -56,8 +60,6 @@ class PlaceSerializer(EventSpecificSerializer): # TODO rajouter des permissions class ActivityTagSerializer(EventSpecificSerializer): - event = EventSerializer(allow_null=True, read_only=True) - class Meta: model = ActivityTag fields = ('url', 'id', 'name', 'is_public', 'color', 'event') @@ -74,23 +76,41 @@ class ActivityTemplateSerializer(serializers.ModelSerializer): 'min_perm', 'max_perm', 'description', 'remarks', 'tags',) def update(self, instance, validated_data): + """ + @tags comportement attendu : si l'id existe déjà on ne change pas + les autres champs et si l'id n'existe pas on le créé + """ tags_data = validated_data.pop('tags') validated_data.pop('event_pk') event = instance.event [setattr(instance, key, value) for key, value in validated_data.items()] instance.save() + # TODO: en fonction de si backbone envoie un `id` ou non lorsque le tag + # n'existe pas encore il faudra faire un premier passage sur `tags` i + # pour s'assurer que le get ne foire pas le get si, par exemple, le tag + # été modifié entre temps dans la base de donnée (mais pas sur la + # classe backbone tags = [ActivityTag.objects.get_or_create(event=event, **tag_data)[0] for tag_data in tags_data] - instance.tags = tags + instance.tags.set(tags) return instance def create(self, validated_data): + """ + @tags comportement attendu : si l'id existe déjà on ne change pas + les autres champs et si l'id n'existe pas on le créé + """ tags_data = validated_data.pop('tags') event_pk = validated_data.pop('event_pk') event = event_pk and get_object_or_404(Event, id=event_pk) or None activity_template = ActivityTemplate.objects.create(event=event, **validated_data) + # TODO: en fonction de si backbone envoie un `id` ou non lorsque le tag + # n'existe pas encore il faudra faire un premier passage sur `tags` i + # pour s'assurer que le get ne foire pas le get si, par exemple, le tag + # été modifié entre temps dans la base de donnée (mais pas sur la + # classe backbone tags = [ActivityTag.objects.get_or_create(event=event, **tag_data)[0] for tag_data in tags_data] activity_template.tags = tags diff --git a/api/event/views.py b/api/event/views.py index 7add4bf..b360583 100644 --- a/api/event/views.py +++ b/api/event/views.py @@ -15,11 +15,11 @@ class EventSpecificViewSet(ModelViewSet): """ ViewSet that returns : * rootlevel objects if no Event is specified - * OR objects related to the specidied event + * OR objects related to the specified event AND root level objects if an event is specified it passes the event_pk to the save method. Works fine with serializers.EventSpecificSerializer - Useful for models that exetends EventSpecificMixin + Useful for models that extends EventSpecificMixin """ def get_queryset(self): """ diff --git a/api/test_event.py b/api/test_event.py index 92c8b0b..1796608 100644 --- a/api/test_event.py +++ b/api/test_event.py @@ -5,7 +5,7 @@ from rest_framework.test import APITestCase from event.models import Event, Place, ActivityTag, ActivityTemplate from api.event.serializers import ActivityTemplateSerializer, EventSerializer -from api.test_mixins import EventBasedModelMixin, EventSpecificMixin,\ +from api.test_mixins import EventBasedModelTestMixin, EventSpecificTestMixin,\ ModelTestMixin User = get_user_model() @@ -22,7 +22,7 @@ class EventTest(ModelTestMixin, APITestCase): serializer = EventSerializer -class ActivityTemplateTest(EventBasedModelMixin, APITestCase): +class ActivityTemplateTest(EventBasedModelTestMixin, APITestCase): model = ActivityTemplate base_name = 'event-activitytemplate' initial_count = 1 @@ -44,7 +44,7 @@ class ActivityTemplateTest(EventBasedModelMixin, APITestCase): self.assertEqual(instance.tags.count(), 2) -class ActivityTest(EventBasedModelMixin, APITestCase): +class ActivityTest(EventBasedModelTestMixin, APITestCase): model = ActivityTemplate base_name = 'event-activity' initial_count = 1 @@ -66,13 +66,13 @@ class ActivityTest(EventBasedModelMixin, APITestCase): self.assertEqual(instance.tags.count(), 2) -class EventSpecficTagTest(EventSpecificMixin, APITestCase): +class EventSpecficTagTest(EventSpecificTestMixin, APITestCase): model = ActivityTag root_base_name = 'activitytag' event_base_name = 'event-activitytag' -class EventSpecficPlaceTest(EventSpecificMixin, APITestCase): +class EventSpecficPlaceTest(EventSpecificTestMixin, APITestCase): model = Place root_base_name = 'place' event_base_name = 'event-place' diff --git a/api/test_mixins.py b/api/test_mixins.py index 1b19100..39ef4d4 100644 --- a/api/test_mixins.py +++ b/api/test_mixins.py @@ -14,6 +14,9 @@ User = get_user_model() class DataBaseMixin(object): + """ + provides a datatabse for API tests + """ @classmethod def setUpTestData(cls): # Users @@ -61,23 +64,35 @@ class DataBaseMixin(object): self.act1.tags.add(self.tag1) -class EventBasedModelMixin(DataBaseMixin): +class EventBasedModelTestMixin(DataBaseMixin): """ Note : need to define : `model`, `base_name`, `initial_count`, `data_creation`, `instance_name`, `field_tested`, `serializer` + + tests for models served by the API that are related to an event + and whose API urls are nested under ../event//%model """ - def test_user_create_extra(self): + def user_create_extra(self): + """ + extra test in creation by a permited user + """ pass def pre_update_extra(self, data): + """ + extra modification for the data sent for update + """ return data def post_update_extra(self): + """ + extra test for updated model + """ pass def test_user_create(self): """ - ensure we can create a new %model object using API + ensure a permited user can create a new %model object using API """ data = getattr(self, self.data_creation) @@ -92,11 +107,11 @@ class EventBasedModelMixin(DataBaseMixin): self.assertEqual(self.model.objects.get(id=self.initial_count+1).event, self.event1) - self.test_create_extra() + self.user_create_extra() def test_user_update(self): """ - ensure we can update an %model object using API + ensure a permited user can update a new %model object using API """ instance = getattr(self, self.instance_name) factory = APIRequestFactory() @@ -126,7 +141,7 @@ class EventBasedModelMixin(DataBaseMixin): def test_user_delete(self): """ - ensure we can update an %model object using API + ensure a permited user can delete a new %model object using API """ instance = getattr(self, self.instance_name) @@ -145,11 +160,13 @@ class EventBasedModelMixin(DataBaseMixin): # TODO rajouter la gestion des permissions dans le Mixin # TODO rajouter un test pour s'assurer que les personnes non # connectées ne peuvent pas create/update/delete -class EventSpecificMixin(object): +class EventSpecificTestMixin(object): """ Tests is the EventSpecifics querysets are rendered correctly using the API Note : need to define : `model`, `root_base_name` and `event_base_name` + + tests for models served by the API that inherit EventSpecificMixin """ @classmethod def setUpTestData(cls): @@ -180,6 +197,12 @@ class EventSpecificMixin(object): self.place_event = Place.objects.create(**self.place_event_data) def test_lists(self): + """ + ensure that only root-level models are served under + api/%root_base_name/ + and that root-level and event-level models are served under + api/event//%event_base_name/ + """ event_id = self.event1.id root_count = self.model.objects.filter(event=None).count() event_count = (self.model.objects @@ -208,10 +231,13 @@ class ModelTestMixin(DataBaseMixin): """ Note : need to define : `model`, `base_name`, `instance_name`, `field_tested`, `serializer` + + generic mixin for testing creation/update/delete + of models served by the API """ def test_user_create(self): """ - ensure we can create a new %model object using API + ensure a permited user can create a new %model object using API """ data = getattr(self, self.data_creation) initial_count = self.model.objects.count() @@ -229,7 +255,7 @@ class ModelTestMixin(DataBaseMixin): def test_user_update(self): """ - ensure we can update an %model object using API + ensure a permited user can update a new %model object using API """ instance = getattr(self, self.instance_name) factory = APIRequestFactory() @@ -253,7 +279,7 @@ class ModelTestMixin(DataBaseMixin): def test_user_delete(self): """ - ensure we can update an %model object using API + ensure a permited user can delete a new %model object using API """ instance = getattr(self, self.instance_name) initial_count = self.model.objects.count() diff --git a/communication/tests.py b/communication/tests.py index e9a1061..800eac6 100644 --- a/communication/tests.py +++ b/communication/tests.py @@ -26,7 +26,7 @@ class SubscriptionTest(TestCase): title='TestEvent', slug='test', created_by=cls.root, - creation_date=timezone.now(), + created_at=timezone.now(), description="Ceci est un test", beginning_date=timezone.now() + timedelta(days=30), diff --git a/event/migrations/0003_auto_20170726_1116.py b/event/migrations/0003_auto_20170726_1116.py new file mode 100644 index 0000000..e776702 --- /dev/null +++ b/event/migrations/0003_auto_20170726_1116.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.3 on 2017-07-26 11:16 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('event', '0002_auto_20170723_1419'), + ] + + operations = [ + migrations.RenameField( + model_name='event', + old_name='creation_date', + new_name='created_at', + ), + migrations.AlterField( + model_name='activity', + name='parent', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='event.ActivityTemplate'), + ), + ] diff --git a/event/models.py b/event/models.py index 66db679..eebe9b3 100644 --- a/event/models.py +++ b/event/models.py @@ -24,7 +24,7 @@ class Event(SubscriptionMixin, models.Model): related_name="created_events", editable=False, ) - creation_date = models.DateTimeField( + created_at = models.DateTimeField( _('date de création'), auto_now_add=True, ) @@ -167,6 +167,7 @@ class Activity(AbstractActivityTemplate): ActivityTemplate, related_name="children", blank=True, + null=True, ) staff = models.ManyToManyField( User, @@ -178,7 +179,8 @@ class Activity(AbstractActivityTemplate): end = models.DateTimeField(_("heure de fin")) def get_herited(self, attrname): - inherited_fields = [f.name for f in ActivityTemplate._meta.get_fields()] + inherited_fields = [f.name for f in + ActivityTemplate._meta.get_fields()] m2m_fields = [f.name for f in ActivityTemplate._meta.get_fields() if f.many_to_many] attr = getattr(self, attrname) diff --git a/event/tests.py b/event/tests.py index f94b5ab..36dee90 100644 --- a/event/tests.py +++ b/event/tests.py @@ -22,7 +22,7 @@ class ActivityInheritanceTest(TestCase): title='La Nuit 2042', slug='nuit42', created_by=cls.erkan, - creation_date=timezone.now(), + created_at=timezone.now(), description="La nuit c'est lol", beginning_date=timezone.now() + timedelta(days=30),