Multiple serializers for Entry based on type (#490)
* entry types with appropriate serializers and URL endpoints
This commit is contained in:
Родитель
a3fefe8ef8
Коммит
f4c237a106
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.20 on 2019-05-09 00:29
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('entries', '0024_entry_entry_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='entry',
|
||||
name='content_url',
|
||||
field=models.URLField(blank=True),
|
||||
),
|
||||
]
|
|
@ -87,10 +87,11 @@ class Entry(models.Model):
|
|||
max_length=20,
|
||||
choices=ENTRY_TYPES,
|
||||
default=ENTRY_TYPES[0][0],
|
||||
null=False,
|
||||
)
|
||||
title = models.CharField(max_length=140)
|
||||
content_url = models.URLField()
|
||||
content_url = models.URLField(
|
||||
blank=True,
|
||||
)
|
||||
|
||||
# optional fields
|
||||
description = models.CharField(max_length=600, blank=True)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
from .base import (
|
||||
associate_entry_with_creator_data,
|
||||
CreatableSlugRelatedField,
|
||||
ModerationStateSerializer,
|
||||
EntryBaseSerializer,
|
||||
EntryWithV1CreatorsBaseSerializer,
|
||||
EntryWithCreatorsBaseSerializer,
|
||||
EntrySerializer,
|
||||
EntrySerializerWithCreators,
|
||||
EntrySerializerWithV1Creators,
|
||||
)
|
||||
|
||||
from .curriculum import CurriculumEntrySerializer
|
||||
from .info import InfoEntrySerializer
|
||||
from .news import NewsEntrySerializer
|
||||
from .project import ProjectEntrySerializer
|
||||
from .session import SessionEntrySerializer
|
||||
|
||||
__all__ = [
|
||||
'associate_entry_with_creator_data',
|
||||
'CreatableSlugRelatedField',
|
||||
'ModerationStateSerializer',
|
||||
'EntryBaseSerializer',
|
||||
'EntryWithV1CreatorsBaseSerializer',
|
||||
'EntryWithCreatorsBaseSerializer',
|
||||
'EntrySerializer',
|
||||
'EntrySerializerWithCreators',
|
||||
'EntrySerializerWithV1Creators',
|
||||
'CurriculumEntrySerializer',
|
||||
'InfoEntrySerializer',
|
||||
'NewsEntrySerializer',
|
||||
'ProjectEntrySerializer',
|
||||
'SessionEntrySerializer',
|
||||
]
|
|
@ -59,6 +59,12 @@ class EntryBaseSerializer(serializers.ModelSerializer):
|
|||
Serializes an entry with minimal information
|
||||
"""
|
||||
|
||||
# As an optional-for-some-types-of-entries, this
|
||||
# field is required "by default" in this serializer,
|
||||
# and marked as "not required" for the various entry
|
||||
# types that don't actually need it.
|
||||
content_url = serializers.URLField(required=True)
|
||||
|
||||
is_bookmarked = serializers.SerializerMethodField()
|
||||
|
||||
def get_is_bookmarked(self, instance):
|
|
@ -0,0 +1,5 @@
|
|||
from .base import EntrySerializerWithCreators
|
||||
|
||||
|
||||
class CurriculumEntrySerializer(EntrySerializerWithCreators):
|
||||
pass
|
|
@ -0,0 +1,5 @@
|
|||
from .base import EntrySerializerWithCreators
|
||||
|
||||
|
||||
class InfoEntrySerializer(EntrySerializerWithCreators):
|
||||
pass
|
|
@ -0,0 +1,9 @@
|
|||
from .base import EntrySerializerWithCreators
|
||||
|
||||
|
||||
class NewsEntrySerializer(EntrySerializerWithCreators):
|
||||
|
||||
class Meta(EntrySerializerWithCreators.Meta):
|
||||
exclude = EntrySerializerWithCreators.Meta.exclude + (
|
||||
'help_types',
|
||||
)
|
|
@ -0,0 +1,7 @@
|
|||
from rest_framework import serializers
|
||||
from .base import EntrySerializerWithCreators
|
||||
|
||||
|
||||
class ProjectEntrySerializer(EntrySerializerWithCreators):
|
||||
|
||||
content_url = serializers.URLField(required=False)
|
|
@ -0,0 +1,14 @@
|
|||
from rest_framework import serializers
|
||||
from .base import EntrySerializerWithCreators
|
||||
|
||||
|
||||
class SessionEntrySerializer(EntrySerializerWithCreators):
|
||||
|
||||
content_url = serializers.URLField(required=False)
|
||||
|
||||
class Meta(EntrySerializerWithCreators.Meta):
|
||||
exclude = EntrySerializerWithCreators.Meta.exclude + (
|
||||
'thumbnail',
|
||||
'get_involved',
|
||||
'get_involved_url',
|
||||
)
|
|
@ -0,0 +1,173 @@
|
|||
from pulseapi.entries.models import Entry
|
||||
from pulseapi.tests import PulseMemberTestCase
|
||||
|
||||
|
||||
def get_full_payload():
|
||||
return {
|
||||
'title': 'test test_post_full_entry',
|
||||
'description': 'description full entry',
|
||||
'tags': ['tag1', 'tag2'],
|
||||
'interest': 'interest field',
|
||||
'get_involved': 'get involved text field',
|
||||
'get_involved_url': 'http://example.com/getinvolved',
|
||||
'content_url': 'http://example.com/',
|
||||
'internal_notes': 'Some internal notes',
|
||||
'featured': True,
|
||||
'issues': ['Decentralization'],
|
||||
'related_creators': [
|
||||
{'name': 'Pomax'},
|
||||
{'name': 'Alan'}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
class TestMemberEntryView(PulseMemberTestCase):
|
||||
def test_base_serializer(self):
|
||||
postresponse = self.client.post(
|
||||
'/api/pulse/entries/',
|
||||
data=self.generatePostPayload(
|
||||
data=get_full_payload()
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(postresponse.status_code, 200)
|
||||
|
||||
def test_base_serializer_without_content_url(self):
|
||||
postresponse = self.client.post(
|
||||
'/api/pulse/entries/',
|
||||
data=self.generatePostPayload(
|
||||
data=get_full_payload(),
|
||||
exclude=['title']
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(postresponse.status_code, 400)
|
||||
|
||||
def test_curriculum_serializer(self):
|
||||
postresponse = self.client.post(
|
||||
'/api/pulse/entries/curriculum/',
|
||||
data=self.generatePostPayload(
|
||||
data=get_full_payload()
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(postresponse.status_code, 200)
|
||||
|
||||
def test_curriculum_serializer_without_content_url(self):
|
||||
postresponse = self.client.post(
|
||||
'/api/pulse/entries/curriculum/',
|
||||
data=self.generatePostPayload(
|
||||
data=get_full_payload(),
|
||||
exclude=['content_url']
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(postresponse.status_code, 400)
|
||||
|
||||
def test_info_serializer(self):
|
||||
postresponse = self.client.post(
|
||||
'/api/pulse/entries/info/',
|
||||
data=self.generatePostPayload(
|
||||
data=get_full_payload()
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(postresponse.status_code, 200)
|
||||
|
||||
def test_info_serializer_without_content_url(self):
|
||||
postresponse = self.client.post(
|
||||
'/api/pulse/entries/info/',
|
||||
data=self.generatePostPayload(
|
||||
data=get_full_payload(),
|
||||
exclude=['content_url']
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(postresponse.status_code, 400)
|
||||
|
||||
def test_news_serializer(self):
|
||||
postresponse = self.client.post(
|
||||
'/api/pulse/entries/news/',
|
||||
data=self.generatePostPayload(
|
||||
data=get_full_payload()
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(postresponse.status_code, 200)
|
||||
|
||||
def test_news_serializer_without_content_url(self):
|
||||
postresponse = self.client.post(
|
||||
'/api/pulse/entries/news/',
|
||||
data=self.generatePostPayload(
|
||||
data=get_full_payload(),
|
||||
exclude=['content_url']
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(postresponse.status_code, 400)
|
||||
|
||||
def test_project_serializer(self):
|
||||
postresponse = self.client.post(
|
||||
'/api/pulse/entries/project/',
|
||||
data=self.generatePostPayload(
|
||||
data=get_full_payload()
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(postresponse.status_code, 200)
|
||||
|
||||
def test_project_serializer_without_content_url(self):
|
||||
postresponse = self.client.post(
|
||||
'/api/pulse/entries/project/',
|
||||
data=self.generatePostPayload(
|
||||
data=get_full_payload(),
|
||||
exclude=['content_url']
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(postresponse.status_code, 200)
|
||||
|
||||
def test_session_serializer(self):
|
||||
postresponse = self.client.post(
|
||||
'/api/pulse/entries/session/',
|
||||
data=self.generatePostPayload(
|
||||
data=get_full_payload()
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(postresponse.status_code, 200)
|
||||
|
||||
def test_session_serializer_without_content_url(self):
|
||||
postresponse = self.client.post(
|
||||
'/api/pulse/entries/session/',
|
||||
data=self.generatePostPayload(
|
||||
data=get_full_payload(),
|
||||
exclude=['content_url']
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(postresponse.status_code, 200)
|
||||
|
||||
def test_session_serializer_without_content_url_with_thumbnail(self):
|
||||
payload = get_full_payload()
|
||||
|
||||
payload['thumbnail'] = {
|
||||
'name': 'myfile.jpg',
|
||||
'base64':
|
||||
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg==',
|
||||
}
|
||||
|
||||
postresponse = self.client.post(
|
||||
'/api/pulse/entries/session/',
|
||||
data=self.generatePostPayload(
|
||||
data=payload
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(postresponse.status_code, 200)
|
||||
|
||||
entry = Entry.objects.last()
|
||||
|
||||
self.assertEqual(str(entry.thumbnail), '')
|
||||
self.assertEqual(entry.get_involved, '')
|
||||
self.assertEqual(entry.get_involved_url, '')
|
|
@ -8,6 +8,11 @@ from pulseapi.entries.views import (
|
|||
BookmarkedEntries,
|
||||
ModerationStateView,
|
||||
EntriesListView,
|
||||
ProjectEntriesListView,
|
||||
NewsEntriesListView,
|
||||
CurriculumEntriesListView,
|
||||
InfoEntriesListView,
|
||||
SessionEntriesListView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
|
@ -16,6 +21,31 @@ urlpatterns = [
|
|||
EntriesListView.as_view(),
|
||||
name='entries-list'
|
||||
),
|
||||
url(
|
||||
'curriculum/',
|
||||
CurriculumEntriesListView.as_view(),
|
||||
name='curriculum-entries'
|
||||
),
|
||||
url(
|
||||
'info/',
|
||||
InfoEntriesListView.as_view(),
|
||||
name='info-entries'
|
||||
),
|
||||
url(
|
||||
'news/',
|
||||
NewsEntriesListView.as_view(),
|
||||
name='news-entries'
|
||||
),
|
||||
url(
|
||||
'project/',
|
||||
ProjectEntriesListView.as_view(),
|
||||
name='project-entries'
|
||||
),
|
||||
url(
|
||||
'session/',
|
||||
SessionEntriesListView.as_view(),
|
||||
name='session-entries'
|
||||
),
|
||||
url(
|
||||
'bookmarks/',
|
||||
BookmarkedEntries.as_view(),
|
||||
|
|
|
@ -22,6 +22,14 @@ from pulseapi.entries.serializers import (
|
|||
EntrySerializerWithCreators,
|
||||
ModerationStateSerializer,
|
||||
)
|
||||
from .serializers import (
|
||||
ProjectEntrySerializer,
|
||||
NewsEntrySerializer,
|
||||
CurriculumEntrySerializer,
|
||||
InfoEntrySerializer,
|
||||
SessionEntrySerializer
|
||||
)
|
||||
|
||||
from pulseapi.profiles.models import UserBookmarks
|
||||
|
||||
from pulseapi.utility.userpermissions import is_staff_address
|
||||
|
@ -469,6 +477,7 @@ class EntriesListView(ListCreateAPIView):
|
|||
data=request_data,
|
||||
context={'user': user},
|
||||
)
|
||||
|
||||
if serializer.is_valid():
|
||||
# ensure that the published_by is always the user doing
|
||||
# the posting, and set 'featured' to false.
|
||||
|
@ -502,3 +511,28 @@ class EntriesListView(ListCreateAPIView):
|
|||
"post validation failed - {}".format(validation_result),
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
|
||||
class ProjectEntriesListView(EntriesListView):
|
||||
def get_serializer_class(self):
|
||||
return ProjectEntrySerializer
|
||||
|
||||
|
||||
class NewsEntriesListView(EntriesListView):
|
||||
def get_serializer_class(self):
|
||||
return NewsEntrySerializer
|
||||
|
||||
|
||||
class CurriculumEntriesListView(EntriesListView):
|
||||
def get_serializer_class(self):
|
||||
return CurriculumEntrySerializer
|
||||
|
||||
|
||||
class InfoEntriesListView(EntriesListView):
|
||||
def get_serializer_class(self):
|
||||
return InfoEntrySerializer
|
||||
|
||||
|
||||
class SessionEntriesListView(EntriesListView):
|
||||
def get_serializer_class(self):
|
||||
return SessionEntrySerializer
|
||||
|
|
|
@ -134,7 +134,7 @@ def generate_default_payload(values):
|
|||
}
|
||||
|
||||
|
||||
def generate_payload(test, data={}, payload=False):
|
||||
def generate_payload(test, data={}, exclude={}, payload=False):
|
||||
values = json.loads(
|
||||
str(test.client.get('/api/pulse/nonce/').content, 'utf-8')
|
||||
)
|
||||
|
@ -145,6 +145,9 @@ def generate_payload(test, data={}, payload=False):
|
|||
for key in data:
|
||||
payload[key] = data[key]
|
||||
|
||||
for key in exclude:
|
||||
del payload[key]
|
||||
|
||||
return json.dumps(payload)
|
||||
|
||||
|
||||
|
@ -173,8 +176,8 @@ class PulseMemberTestCase(TestCase):
|
|||
email="test@example.org"
|
||||
)
|
||||
|
||||
def generatePostPayload(self, data={}):
|
||||
return generate_payload(self, data)
|
||||
def generatePostPayload(self, data={}, exclude=[]):
|
||||
return generate_payload(self, data, exclude)
|
||||
|
||||
|
||||
class PulseStaffTestCase(TestCase):
|
||||
|
@ -190,8 +193,8 @@ class PulseStaffTestCase(TestCase):
|
|||
email="test@mozillafoundation.org"
|
||||
)
|
||||
|
||||
def generatePostPayload(self, data={}):
|
||||
return generate_payload(self, data)
|
||||
def generatePostPayload(self, data={}, exclude=[]):
|
||||
return generate_payload(self, data, exclude)
|
||||
|
||||
|
||||
class PulseModeratorTestCase(TestCase):
|
||||
|
@ -206,8 +209,8 @@ class PulseModeratorTestCase(TestCase):
|
|||
is_moderator=True
|
||||
)
|
||||
|
||||
def generatePostPayload(self, data={}):
|
||||
return generate_payload(self, data)
|
||||
def generatePostPayload(self, data={}, exclude=[]):
|
||||
return generate_payload(self, data, exclude)
|
||||
|
||||
|
||||
class TestAPIVersioning(TestCase):
|
||||
|
|
Загрузка…
Ссылка в новой задаче