Skip to content
This repository was archived by the owner on Jun 6, 2024. It is now read-only.

Commit 2594143

Browse files
committed
Add self and related URL overrides to Schema
1 parent 1691c8a commit 2594143

File tree

5 files changed

+110
-4
lines changed

5 files changed

+110
-4
lines changed

marshmallow_jsonapi/flask.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,42 @@ class Meta:
6060
"""
6161
pass
6262

63+
def __init__(self, *args, **kwargs):
64+
if kwargs.get('self_url', None):
65+
raise TypeError('Use `self_view` instead of `self_url` '
66+
'using the Flask extension.')
67+
if kwargs.get('self_url_kwargs', None):
68+
raise TypeError('Use `self_view_kwargs` instead of '
69+
'`self_url_kwargs` when using the Flask extension.')
70+
if kwargs.get('related_url', None):
71+
raise TypeError('Use `related_view` instead of `related_url` '
72+
'using the Flask extension.')
73+
if kwargs.get('related_url_kwargs', None):
74+
raise TypeError('Use `related_view_kwargs` instead of '
75+
'`related_url_kwargs` when using the Flask extension.')
76+
77+
self_view = kwargs.pop('self_view', None)
78+
self_view_kwargs = kwargs.pop('self_view_kwargs', dict())
79+
related_view = kwargs.pop('related_view', None)
80+
related_view_kwargs = kwargs.pop('related_view_kwargs', dict())
81+
82+
if self_view_kwargs and not self_view:
83+
raise ValueError('Must specify `self_view` keyword when '
84+
'`self_view_kwargs` is specified.')
85+
86+
if related_view_kwargs and not related_view:
87+
raise ValueError('Must specify `related_view` keyword when '
88+
'`related_view_kwargs` is specified.')
89+
90+
if self_view:
91+
kwargs['self_url'] = flask.url_for(self_view, **self_view_kwargs)
92+
if related_view:
93+
kwargs['related_url'] = flask.url_for(related_view,
94+
**related_view_kwargs)
95+
96+
super(Schema, self).__init__(*args, **kwargs)
97+
98+
6399
def generate_url(self, view_name, **kwargs):
64100
"""Generate URL with any kwargs interpolated."""
65101
return flask.url_for(view_name, **kwargs) if view_name else None

marshmallow_jsonapi/schema.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ class Meta:
8686
def __init__(self, *args, **kwargs):
8787
self.include_data = kwargs.pop('include_data', ())
8888
self.resource_identifier = kwargs.pop('resource_identifier', False)
89+
self.override_self_url = kwargs.pop('self_url', None)
90+
self.override_related_url = kwargs.pop('related_url', None)
8991
super(Schema, self).__init__(*args, **kwargs)
9092
if self.include_data:
9193
self.check_relations(self.include_data)
@@ -398,10 +400,21 @@ def get_top_level_links(self, data, many):
398400
if self.opts.self_url_many:
399401
self_link = self.generate_url(self.opts.self_url_many)
400402
else:
401-
if self.opts.self_url:
403+
if self.opts.self_url and data is not None:
402404
self_link = data.get('links', {}).get('self', None)
403405

404-
return {'self': self_link}
406+
if self.override_self_url is not None:
407+
self_link = self.override_self_url
408+
409+
links = dict()
410+
411+
if self_link is not None:
412+
links['self'] = self_link
413+
414+
if self.override_related_url:
415+
links['related'] = self.override_related_url
416+
417+
return links
405418

406419
def get_resource_links(self, item):
407420
"""Hook for adding links to a resource object."""
@@ -419,7 +432,7 @@ def wrap_response(self, data, many):
419432
# may only be included if there is data in the ret
420433
if many or data:
421434
top_level_links = self.get_top_level_links(data, many)
422-
if top_level_links['self']:
435+
if top_level_links:
423436
ret['links'] = top_level_links
424437
return ret
425438

tests/base.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ def get_top_level_links(self, data, many):
4848
self_link = '/authors/'
4949
else:
5050
self_link = '/authors/{}'.format(data['id'])
51-
return {'self': self_link}
51+
links = {'self': self_link}
52+
53+
schema_links = super(AuthorSchema, self).get_top_level_links(data, many)
54+
links.update(schema_links)
55+
56+
return links
5257

5358
class Meta:
5459
type_ = 'people'

tests/test_flask.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,37 @@ def test_self_link_many(self, app, posts):
9999
assert 'links' in data['data'][0]
100100
assert data['data'][0]['links']['self'] == '/posts/{}/'.format(posts[0].id)
101101

102+
@pytest.mark.parametrize('kwargs, exc, msg', [
103+
(dict(self_url='foo'), TypeError, 'self_view'),
104+
(dict(self_url_kwargs='foo'), TypeError, 'self_view_kwargs'),
105+
(dict(related_url='foo'), TypeError, 'related_view'),
106+
(dict(related_url_kwargs='foo'), TypeError, 'related_view_kwargs'),
107+
(dict(self_view_kwargs='foo'), ValueError, 'self_view'),
108+
(dict(related_view_kwargs='foo'), ValueError, 'related_view'),
109+
])
110+
def test_schema_view_override_exceptions(self, kwargs, exc, msg):
111+
with pytest.raises(exc) as exc_info:
112+
self.PostFlaskSchema(**kwargs)
113+
114+
assert msg in exc_info.value.args[0]
115+
116+
def test_view_override(self, app):
117+
# Here we use "wrong" views just to check output.
118+
119+
schema = self.PostFlaskSchema(self_view='posts')
120+
assert schema.override_self_url == '/posts/'
121+
122+
schema = self.PostFlaskSchema(
123+
self_view='author_detail', self_view_kwargs={'author_id': 1})
124+
assert schema.override_self_url == '/authors/1'
125+
126+
schema = self.PostFlaskSchema(related_view='posts')
127+
assert schema.override_related_url == '/posts/'
128+
129+
schema = self.PostFlaskSchema(
130+
related_view='author_detail', related_view_kwargs={'author_id': 1})
131+
assert schema.override_related_url == '/authors/1'
132+
102133
def test_schema_with_empty_relationship(self, app, post_with_null_author):
103134
data = unpack(self.PostAuthorFlaskSchema().dump(post_with_null_author))
104135
assert 'relationships' not in data

tests/test_schema.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,3 +752,24 @@ class Meta:
752752

753753
assert assert_relationship_error('author', errors['errors'])
754754
assert assert_relationship_error('comments', errors['errors'])
755+
756+
757+
class TestOverrideUrls(object):
758+
def test_self_url(self, author):
759+
url = '/articles/1/author'
760+
schema = AuthorSchema(self_url=url)
761+
data = schema.dump(author).data
762+
763+
assert data['links']['self'] == url
764+
765+
def test_related_url(self, author):
766+
self_url = '/articles/1/relationships/author'
767+
related_url = '/articles/1/author'
768+
schema = AuthorSchema(
769+
resource_identifier=True,
770+
self_url=self_url,
771+
related_url=related_url)
772+
data = schema.dump(author).data
773+
774+
assert data['links']['self'] == self_url
775+
assert data['links']['related'] == related_url

0 commit comments

Comments
 (0)