Merge branch 'Qwann/Serializers' into Qwann/Serializers_event2

This commit is contained in:
Qwann 2017-07-26 15:51:59 +02:00
commit 2412c8344f
8 changed files with 110 additions and 36 deletions

View file

@ -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

View file

@ -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):
"""

View file

@ -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'

View file

@ -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()

View file

@ -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),

View 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'),
),
]

View file

@ -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)

View file

@ -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),