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

Commit b9e99a6

Browse files
committed
Add self and related URL overrides to Schema
1 parent 6428a08 commit b9e99a6

File tree

4 files changed

+112
-4
lines changed

4 files changed

+112
-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
@@ -80,6 +80,8 @@ class Meta:
8080
def __init__(self, *args, **kwargs):
8181
self.include_data = kwargs.pop('include_data', ())
8282
self.resource_identifier = kwargs.pop('resource_identifier', False)
83+
self.override_self_url = kwargs.pop('self_url', None)
84+
self.override_related_url = kwargs.pop('related_url', None)
8385
super(Schema, self).__init__(*args, **kwargs)
8486
if self.include_data:
8587
self.check_relations(self.include_data)
@@ -370,10 +372,21 @@ def get_top_level_links(self, data, many):
370372
if self.opts.self_url_many:
371373
self_link = self.generate_url(self.opts.self_url_many)
372374
else:
373-
if self.opts.self_url:
375+
if self.opts.self_url and data is not None:
374376
self_link = data.get('links', {}).get('self', None)
375377

376-
return {'self': self_link}
378+
if self.override_self_url is not None:
379+
self_link = self.override_self_url
380+
381+
links = dict()
382+
383+
if self_link is not None:
384+
links['self'] = self_link
385+
386+
if self.override_related_url:
387+
links['related'] = self.override_related_url
388+
389+
return links
377390

378391
def get_resource_links(self, item):
379392
"""Hook for adding links to a resource object."""
@@ -391,7 +404,7 @@ def wrap_response(self, data, many):
391404
# may only be included if there is data in the ret
392405
if many or data:
393406
top_level_links = self.get_top_level_links(data, many)
394-
if top_level_links['self']:
407+
if top_level_links:
395408
ret['links'] = top_level_links
396409
return ret
397410

tests/test_flask.py

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

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

tests/test_schema.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,14 @@ def get_top_level_links(self, data, many):
2020
self_link = '/authors/'
2121
else:
2222
self_link = '/authors/{}'.format(data['id'])
23-
return {'self': self_link}
23+
24+
links = {'self': self_link}
25+
26+
schema_links = super(AuthorSchema, self).get_top_level_links(data, many)
27+
links.update(schema_links)
28+
29+
return links
30+
2431

2532
class Meta:
2633
type_ = 'people'
@@ -855,3 +862,24 @@ def test_deserializing_relationship_errors(self):
855862
assert \
856863
self._assert_relationship_error('comments', errors['errors']) is \
857864
True
865+
866+
867+
class TestOverrideUrls(object):
868+
def test_self_url(self, author):
869+
url = '/articles/1/author'
870+
schema = AuthorSchema(self_url=url)
871+
data = schema.dump(author).data
872+
873+
assert data['links']['self'] == url
874+
875+
def test_related_url(self, author):
876+
self_url = '/articles/1/relationships/author'
877+
related_url = '/articles/1/author'
878+
schema = AuthorSchema(
879+
resource_identifier=True,
880+
self_url=self_url,
881+
related_url=related_url)
882+
data = schema.dump(author).data
883+
884+
assert data['links']['self'] == self_url
885+
assert data['links']['related'] == related_url

0 commit comments

Comments
 (0)