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()
|
||||
|
||||
|
||||
# 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
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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/<event_id>/%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_id>/%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()
|
||||
|
|
|
@ -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),
|
||||
|
|
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",
|
||||
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)
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Add table
Reference in a new issue