Skip to content

Commit

Permalink
Add default values, including addition of primary_key, and add corres…
Browse files Browse the repository at this point in the history
…ponding tests.
  • Loading branch information
kaitj91 committed Apr 10, 2017
1 parent 61f6cb0 commit 7afa5d6
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 63 deletions.
37 changes: 23 additions & 14 deletions sqlalchemy_jsonapi/declarative/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,22 @@

class JSONAPISerializer(object):
"""A JSON API serializer that serializes SQLAlchemy models."""
model = None
primary_key = 'id'
fields = []
dasherize = True

def __init__(self):
"""Ensure required members are not defaults."""
if self.model is None:
raise TypeError("Model cannot be of type 'None'.")
if self.primary_key not in self.fields:
raise ValueError(
"Serializer fields must contain primary key '{}'".format(
self.primary_key))

def serialize(self, resources):
"""Serialize resource(s) according to json-api spec."""
try:
self.fields
self.model
self.dasherize
except AttributeError:
raise

if 'id' not in self.fields:
raise ValueError("Serializer fields must contain an 'id'")
serialized = {
'meta': {
'sqlalchemy_jsonapi_version': '4.0.9'
Expand Down Expand Up @@ -53,12 +57,14 @@ def _render_resource(self, resource):
'Resource(s) type must be the same as the serializer model type.')

top_level_members = {}
top_level_members['id'] = str(resource.id)
try:
top_level_members['id'] = str(getattr(resource, self.primary_key))
except AttributeError:
raise
top_level_members['type'] = resource.__tablename__
top_level_members['attributes'] = self._render_attributes(resource)
top_level_members['relationships'] = self._render_relationships(
resource)

return top_level_members

def _render_attributes(self, resource):
Expand All @@ -77,7 +83,7 @@ def _render_attributes(self, resource):
mapped_fields = {x: x for x in self.fields}

for attribute in self.fields:
if attribute == 'id':
if attribute == self.primary_key:
continue
# Per json-api spec, we cannot render foreign keys
# or relationsips in attributes.
Expand All @@ -98,6 +104,7 @@ def _render_relationships(self, resource):
"""Render the resource's relationships."""
relationships = {}
related_models = resource.__mapper__.relationships.keys()
primary_key_val = getattr(resource, self.primary_key)
if self.dasherize:
mapped_relationships = {
x: dasherize(underscore(x)) for x in related_models}
Expand All @@ -108,10 +115,12 @@ def _render_relationships(self, resource):
relationships[mapped_relationships[model]] = {
'links': {
'self': '/{}/{}/relationships/{}'.format(
resource.__tablename__, resource.id,
resource.__tablename__,
primary_key_val,
mapped_relationships[model]),
'related': '/{}/{}/{}'.format(
resource.__tablename__, resource.id,
resource.__tablename__,
primary_key_val,
mapped_relationships[model])
}
}
Expand Down
97 changes: 48 additions & 49 deletions sqlalchemy_jsonapi/unittests/declarative_tests/test_serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,6 @@ class UserSerializer(serializer.JSONAPISerializer):
"""Declarative serializer for User."""
fields = ['id', 'first_name']
model = self.User
dasherize = True

user = self.User(first_name='Sally')
self.session.add(user)
Expand Down Expand Up @@ -340,7 +339,6 @@ class PostSerializer(serializer.JSONAPISerializer):
"""Declarative serializer for Post."""
fields = ['id', 'title']
model = self.Post
dasherize = True

blog_post = self.Post(title='Foo')
self.session.add(blog_post)
Expand Down Expand Up @@ -464,7 +462,6 @@ class UserSerializer(serializer.JSONAPISerializer):
"""Declarative serializer for User."""
fields = ['id']
model = self.Post
dasherize = True

user = self.User(first_name='Sally')
self.session.add(user)
Expand All @@ -475,32 +472,13 @@ class UserSerializer(serializer.JSONAPISerializer):
with self.assertRaises(TypeError):
user_serializer.serialize(user)

def test_serialize_resource_with_missing_id_field(self):
"""An 'id' is required in serializer fields."""

class UserSerializer(serializer.JSONAPISerializer):
"""Declarative serializer for User."""
fields = ['first_name']
model = self.User
dasherize = True

user = self.User(first_name='Sally')
self.session.add(user)
self.session.commit()
user = self.session.query(self.User).get(user.id)

user_serializer = UserSerializer()
with self.assertRaises(ValueError):
user_serializer.serialize(user)

def test_serialize_resource_with_unknown_attribute_in_fields(self):
"""Cannot serialize attributes that are unknown to resource."""

class UserSerializer(serializer.JSONAPISerializer):
"""Declarative serializer for User."""
fields = ['id', 'firsts_names_unknown']
model = self.User
dasherize = True

user = self.User(first_name='Sally')
self.session.add(user)
Expand All @@ -521,7 +499,6 @@ class UserSerializer(serializer.JSONAPISerializer):
"""Declarative serializer for User."""
fields = ['id', 'posts']
model = self.User
dasherize = True

user = self.User(first_name='Sally')
self.session.add(user)
Expand All @@ -542,7 +519,6 @@ class PostSerializer(serializer.JSONAPISerializer):
"""Declarative serializer for Post."""
fields = ['id', 'author_id']
model = self.Post
dasherize = False

blog_post = self.Post(title='Foo')
self.session.add(blog_post)
Expand All @@ -553,12 +529,16 @@ class PostSerializer(serializer.JSONAPISerializer):
with self.assertRaises(AttributeError):
blog_post_serializer.serialize(post)

def test_serialize_resource_with_no_defined_dasherize(self):
"""Serializer requires dasherize member."""
def test_serialize_resource_with_invalid_primary_key(self):
"""Resource cannot have unknown primary key.
The primary key must be an attribute on the resource.
"""

class UserSerializer(serializer.JSONAPISerializer):
"""Declarative serializer for User."""
fields = ['id', 'firsts_names_unknown']
"""Declarative serializer for Post."""
fields = ['unknown_primary_key', 'first_name']
primary_key = 'unknown_primary_key'
model = self.User

user = self.User(first_name='Sally')
Expand All @@ -570,36 +550,55 @@ class UserSerializer(serializer.JSONAPISerializer):
with self.assertRaises(AttributeError):
user_serializer.serialize(user)

def test_serialize_resource_with_no_defined_model(self):

class TestSerializerInstantiationErrors(unittest.TestCase):
"""Test exceptions raised in instantiation of serializer."""

def setUp(self):
"""Configure sqlalchemy and session."""
self.engine = create_engine('sqlite://')
Session = sessionmaker(bind=self.engine)
self.session = Session()
self.Base = declarative_base()

class User(self.Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
first_name = Column(String(50), nullable=False)

self.User = User
self.Base.metadata.create_all(self.engine)

def tearDown(self):
"""Reset the sqlalchemy engine."""
self.Base.metadata.drop_all(self.engine)

def test_serializer_with_no_defined_model(self):
"""Serializer requires model member."""

class UserSerializer(serializer.JSONAPISerializer):
"""Declarative serializer for User."""
fields = ['id', 'firsts_names_unknown']
dasherize = True
fields = ['id']

user = self.User(first_name='Sally')
self.session.add(user)
self.session.commit()
user = self.session.query(self.User).get(user.id)
with self.assertRaises(TypeError):
UserSerializer()

user_serializer = UserSerializer()
with self.assertRaises(AttributeError):
user_serializer.serialize(user)
def test_serializer_with_no_defined_fields(self):
"""At minimum fields must exist."""
class UserSerializer(serializer.JSONAPISerializer):
"""Declarative serializer for User."""
model = self.User

def test_serialize_resource_with_no_defined_fields(self):
"""Serializer requires field member."""
with self.assertRaises(ValueError):
UserSerializer()

def test_serializer_with_missing_id_field(self):
"""An 'id' is required in serializer fields."""

class UserSerializer(serializer.JSONAPISerializer):
"""Declarative serializer for User."""
dasherize = True
fields = ['first_name']
model = self.User

user = self.User(first_name='Sally')
self.session.add(user)
self.session.commit()
user = self.session.query(self.User).get(user.id)

user_serializer = UserSerializer()
with self.assertRaises(AttributeError):
user_serializer.serialize(user)
with self.assertRaises(ValueError):
UserSerializer()

0 comments on commit 7afa5d6

Please sign in to comment.