Merge branch 'Qwann/Serializers' into Qwann/Serializers_event2
This commit is contained in:
commit
2412c8344f
8 changed files with 110 additions and 36 deletions
|
@ -9,14 +9,29 @@ from api.users.serializers import UserSerializer
|
||||||
User = get_user_model()
|
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
|
# Classes utilitaires
|
||||||
class EventSpecificSerializer(serializers.ModelSerializer):
|
class EventSpecificSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
Provide `update` and `create` methods for nested view with an Event
|
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
|
the event id has to be provided in the `save` method
|
||||||
Works fine with view.EventSpecificViewSet
|
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):
|
def update(self, instance, validated_data):
|
||||||
"""
|
"""
|
||||||
Note : does NOT change the event value of the instance
|
Note : does NOT change the event value of the instance
|
||||||
|
@ -36,19 +51,8 @@ class EventSpecificSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
# Serializers
|
# 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
|
# TODO rajouter des permissions
|
||||||
class PlaceSerializer(EventSpecificSerializer):
|
class PlaceSerializer(EventSpecificSerializer):
|
||||||
event = EventSerializer(allow_null=True, read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Place
|
model = Place
|
||||||
fields = ('url', 'id', 'name', 'description', 'event')
|
fields = ('url', 'id', 'name', 'description', 'event')
|
||||||
|
@ -56,8 +60,6 @@ class PlaceSerializer(EventSpecificSerializer):
|
||||||
|
|
||||||
# TODO rajouter des permissions
|
# TODO rajouter des permissions
|
||||||
class ActivityTagSerializer(EventSpecificSerializer):
|
class ActivityTagSerializer(EventSpecificSerializer):
|
||||||
event = EventSerializer(allow_null=True, read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ActivityTag
|
model = ActivityTag
|
||||||
fields = ('url', 'id', 'name', 'is_public', 'color', 'event')
|
fields = ('url', 'id', 'name', 'is_public', 'color', 'event')
|
||||||
|
@ -74,23 +76,41 @@ class ActivityTemplateSerializer(serializers.ModelSerializer):
|
||||||
'min_perm', 'max_perm', 'description', 'remarks', 'tags',)
|
'min_perm', 'max_perm', 'description', 'remarks', 'tags',)
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
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')
|
tags_data = validated_data.pop('tags')
|
||||||
validated_data.pop('event_pk')
|
validated_data.pop('event_pk')
|
||||||
event = instance.event
|
event = instance.event
|
||||||
[setattr(instance, key, value)
|
[setattr(instance, key, value)
|
||||||
for key, value in validated_data.items()]
|
for key, value in validated_data.items()]
|
||||||
instance.save()
|
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]
|
tags = [ActivityTag.objects.get_or_create(event=event, **tag_data)[0]
|
||||||
for tag_data in tags_data]
|
for tag_data in tags_data]
|
||||||
instance.tags = tags
|
instance.tags.set(tags)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def create(self, validated_data):
|
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')
|
tags_data = validated_data.pop('tags')
|
||||||
event_pk = validated_data.pop('event_pk')
|
event_pk = validated_data.pop('event_pk')
|
||||||
event = event_pk and get_object_or_404(Event, id=event_pk) or None
|
event = event_pk and get_object_or_404(Event, id=event_pk) or None
|
||||||
activity_template = ActivityTemplate.objects.create(event=event,
|
activity_template = ActivityTemplate.objects.create(event=event,
|
||||||
**validated_data)
|
**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]
|
tags = [ActivityTag.objects.get_or_create(event=event, **tag_data)[0]
|
||||||
for tag_data in tags_data]
|
for tag_data in tags_data]
|
||||||
activity_template.tags = tags
|
activity_template.tags = tags
|
||||||
|
|
|
@ -15,11 +15,11 @@ class EventSpecificViewSet(ModelViewSet):
|
||||||
"""
|
"""
|
||||||
ViewSet that returns :
|
ViewSet that returns :
|
||||||
* rootlevel objects if no Event is specified
|
* 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
|
AND root level objects
|
||||||
if an event is specified it passes the event_pk
|
if an event is specified it passes the event_pk
|
||||||
to the save method. Works fine with serializers.EventSpecificSerializer
|
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):
|
def get_queryset(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -5,7 +5,7 @@ from rest_framework.test import APITestCase
|
||||||
from event.models import Event, Place, ActivityTag, ActivityTemplate
|
from event.models import Event, Place, ActivityTag, ActivityTemplate
|
||||||
|
|
||||||
from api.event.serializers import ActivityTemplateSerializer, EventSerializer
|
from api.event.serializers import ActivityTemplateSerializer, EventSerializer
|
||||||
from api.test_mixins import EventBasedModelMixin, EventSpecificMixin,\
|
from api.test_mixins import EventBasedModelTestMixin, EventSpecificTestMixin,\
|
||||||
ModelTestMixin
|
ModelTestMixin
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
@ -22,7 +22,7 @@ class EventTest(ModelTestMixin, APITestCase):
|
||||||
serializer = EventSerializer
|
serializer = EventSerializer
|
||||||
|
|
||||||
|
|
||||||
class ActivityTemplateTest(EventBasedModelMixin, APITestCase):
|
class ActivityTemplateTest(EventBasedModelTestMixin, APITestCase):
|
||||||
model = ActivityTemplate
|
model = ActivityTemplate
|
||||||
base_name = 'event-activitytemplate'
|
base_name = 'event-activitytemplate'
|
||||||
initial_count = 1
|
initial_count = 1
|
||||||
|
@ -44,7 +44,7 @@ class ActivityTemplateTest(EventBasedModelMixin, APITestCase):
|
||||||
self.assertEqual(instance.tags.count(), 2)
|
self.assertEqual(instance.tags.count(), 2)
|
||||||
|
|
||||||
|
|
||||||
class ActivityTest(EventBasedModelMixin, APITestCase):
|
class ActivityTest(EventBasedModelTestMixin, APITestCase):
|
||||||
model = ActivityTemplate
|
model = ActivityTemplate
|
||||||
base_name = 'event-activity'
|
base_name = 'event-activity'
|
||||||
initial_count = 1
|
initial_count = 1
|
||||||
|
@ -66,13 +66,13 @@ class ActivityTest(EventBasedModelMixin, APITestCase):
|
||||||
self.assertEqual(instance.tags.count(), 2)
|
self.assertEqual(instance.tags.count(), 2)
|
||||||
|
|
||||||
|
|
||||||
class EventSpecficTagTest(EventSpecificMixin, APITestCase):
|
class EventSpecficTagTest(EventSpecificTestMixin, APITestCase):
|
||||||
model = ActivityTag
|
model = ActivityTag
|
||||||
root_base_name = 'activitytag'
|
root_base_name = 'activitytag'
|
||||||
event_base_name = 'event-activitytag'
|
event_base_name = 'event-activitytag'
|
||||||
|
|
||||||
|
|
||||||
class EventSpecficPlaceTest(EventSpecificMixin, APITestCase):
|
class EventSpecficPlaceTest(EventSpecificTestMixin, APITestCase):
|
||||||
model = Place
|
model = Place
|
||||||
root_base_name = 'place'
|
root_base_name = 'place'
|
||||||
event_base_name = 'event-place'
|
event_base_name = 'event-place'
|
||||||
|
|
|
@ -14,6 +14,9 @@ User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
class DataBaseMixin(object):
|
class DataBaseMixin(object):
|
||||||
|
"""
|
||||||
|
provides a datatabse for API tests
|
||||||
|
"""
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
# Users
|
# Users
|
||||||
|
@ -61,23 +64,35 @@ class DataBaseMixin(object):
|
||||||
self.act1.tags.add(self.tag1)
|
self.act1.tags.add(self.tag1)
|
||||||
|
|
||||||
|
|
||||||
class EventBasedModelMixin(DataBaseMixin):
|
class EventBasedModelTestMixin(DataBaseMixin):
|
||||||
"""
|
"""
|
||||||
Note : need to define : `model`, `base_name`, `initial_count`,
|
Note : need to define : `model`, `base_name`, `initial_count`,
|
||||||
`data_creation`, `instance_name`, `field_tested`, `serializer`
|
`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/<event_id>/%model
|
||||||
"""
|
"""
|
||||||
def test_user_create_extra(self):
|
def user_create_extra(self):
|
||||||
|
"""
|
||||||
|
extra test in creation by a permited user
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def pre_update_extra(self, data):
|
def pre_update_extra(self, data):
|
||||||
|
"""
|
||||||
|
extra modification for the data sent for update
|
||||||
|
"""
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def post_update_extra(self):
|
def post_update_extra(self):
|
||||||
|
"""
|
||||||
|
extra test for updated model
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_user_create(self):
|
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)
|
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.assertEqual(self.model.objects.get(id=self.initial_count+1).event,
|
||||||
self.event1)
|
self.event1)
|
||||||
|
|
||||||
self.test_create_extra()
|
self.user_create_extra()
|
||||||
|
|
||||||
def test_user_update(self):
|
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)
|
instance = getattr(self, self.instance_name)
|
||||||
factory = APIRequestFactory()
|
factory = APIRequestFactory()
|
||||||
|
@ -126,7 +141,7 @@ class EventBasedModelMixin(DataBaseMixin):
|
||||||
|
|
||||||
def test_user_delete(self):
|
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)
|
instance = getattr(self, self.instance_name)
|
||||||
|
|
||||||
|
@ -145,11 +160,13 @@ class EventBasedModelMixin(DataBaseMixin):
|
||||||
# TODO rajouter la gestion des permissions dans le Mixin
|
# TODO rajouter la gestion des permissions dans le Mixin
|
||||||
# TODO rajouter un test pour s'assurer que les personnes non
|
# TODO rajouter un test pour s'assurer que les personnes non
|
||||||
# connectées ne peuvent pas create/update/delete
|
# connectées ne peuvent pas create/update/delete
|
||||||
class EventSpecificMixin(object):
|
class EventSpecificTestMixin(object):
|
||||||
"""
|
"""
|
||||||
Tests is the EventSpecifics querysets are rendered correctly
|
Tests is the EventSpecifics querysets are rendered correctly
|
||||||
using the API
|
using the API
|
||||||
Note : need to define : `model`, `root_base_name` and `event_base_name`
|
Note : need to define : `model`, `root_base_name` and `event_base_name`
|
||||||
|
|
||||||
|
tests for models served by the API that inherit EventSpecificMixin
|
||||||
"""
|
"""
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
@ -180,6 +197,12 @@ class EventSpecificMixin(object):
|
||||||
self.place_event = Place.objects.create(**self.place_event_data)
|
self.place_event = Place.objects.create(**self.place_event_data)
|
||||||
|
|
||||||
def test_lists(self):
|
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_id>/%event_base_name/
|
||||||
|
"""
|
||||||
event_id = self.event1.id
|
event_id = self.event1.id
|
||||||
root_count = self.model.objects.filter(event=None).count()
|
root_count = self.model.objects.filter(event=None).count()
|
||||||
event_count = (self.model.objects
|
event_count = (self.model.objects
|
||||||
|
@ -208,10 +231,13 @@ class ModelTestMixin(DataBaseMixin):
|
||||||
"""
|
"""
|
||||||
Note : need to define : `model`, `base_name`,
|
Note : need to define : `model`, `base_name`,
|
||||||
`instance_name`, `field_tested`, `serializer`
|
`instance_name`, `field_tested`, `serializer`
|
||||||
|
|
||||||
|
generic mixin for testing creation/update/delete
|
||||||
|
of models served by the API
|
||||||
"""
|
"""
|
||||||
def test_user_create(self):
|
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)
|
data = getattr(self, self.data_creation)
|
||||||
initial_count = self.model.objects.count()
|
initial_count = self.model.objects.count()
|
||||||
|
@ -229,7 +255,7 @@ class ModelTestMixin(DataBaseMixin):
|
||||||
|
|
||||||
def test_user_update(self):
|
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)
|
instance = getattr(self, self.instance_name)
|
||||||
factory = APIRequestFactory()
|
factory = APIRequestFactory()
|
||||||
|
@ -253,7 +279,7 @@ class ModelTestMixin(DataBaseMixin):
|
||||||
|
|
||||||
def test_user_delete(self):
|
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)
|
instance = getattr(self, self.instance_name)
|
||||||
initial_count = self.model.objects.count()
|
initial_count = self.model.objects.count()
|
||||||
|
|
|
@ -26,7 +26,7 @@ class SubscriptionTest(TestCase):
|
||||||
title='TestEvent',
|
title='TestEvent',
|
||||||
slug='test',
|
slug='test',
|
||||||
created_by=cls.root,
|
created_by=cls.root,
|
||||||
creation_date=timezone.now(),
|
created_at=timezone.now(),
|
||||||
description="Ceci est un test",
|
description="Ceci est un test",
|
||||||
beginning_date=timezone.now()
|
beginning_date=timezone.now()
|
||||||
+ timedelta(days=30),
|
+ timedelta(days=30),
|
||||||
|
|
26
event/migrations/0003_auto_20170726_1116.py
Normal file
26
event/migrations/0003_auto_20170726_1116.py
Normal file
|
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -24,7 +24,7 @@ class Event(SubscriptionMixin, models.Model):
|
||||||
related_name="created_events",
|
related_name="created_events",
|
||||||
editable=False,
|
editable=False,
|
||||||
)
|
)
|
||||||
creation_date = models.DateTimeField(
|
created_at = models.DateTimeField(
|
||||||
_('date de création'),
|
_('date de création'),
|
||||||
auto_now_add=True,
|
auto_now_add=True,
|
||||||
)
|
)
|
||||||
|
@ -167,6 +167,7 @@ class Activity(AbstractActivityTemplate):
|
||||||
ActivityTemplate,
|
ActivityTemplate,
|
||||||
related_name="children",
|
related_name="children",
|
||||||
blank=True,
|
blank=True,
|
||||||
|
null=True,
|
||||||
)
|
)
|
||||||
staff = models.ManyToManyField(
|
staff = models.ManyToManyField(
|
||||||
User,
|
User,
|
||||||
|
@ -178,7 +179,8 @@ class Activity(AbstractActivityTemplate):
|
||||||
end = models.DateTimeField(_("heure de fin"))
|
end = models.DateTimeField(_("heure de fin"))
|
||||||
|
|
||||||
def get_herited(self, attrname):
|
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()
|
m2m_fields = [f.name for f in ActivityTemplate._meta.get_fields()
|
||||||
if f.many_to_many]
|
if f.many_to_many]
|
||||||
attr = getattr(self, attrname)
|
attr = getattr(self, attrname)
|
||||||
|
|
|
@ -22,7 +22,7 @@ class ActivityInheritanceTest(TestCase):
|
||||||
title='La Nuit 2042',
|
title='La Nuit 2042',
|
||||||
slug='nuit42',
|
slug='nuit42',
|
||||||
created_by=cls.erkan,
|
created_by=cls.erkan,
|
||||||
creation_date=timezone.now(),
|
created_at=timezone.now(),
|
||||||
description="La nuit c'est lol",
|
description="La nuit c'est lol",
|
||||||
beginning_date=timezone.now()
|
beginning_date=timezone.now()
|
||||||
+ timedelta(days=30),
|
+ timedelta(days=30),
|
||||||
|
|
Loading…
Add table
Reference in a new issue