flask-rest-jsonapi
flask-rest-jsonapi copied to clipboard
"First example" doc questions and best practices
Not sure where to post a question regarding best practices of this library, so I thought I'd include an issue.
In the First example docs, the SQL alchemy models specify a Person, the computers and the relationships between them. So far, so good.
At the end the example, there are two APIS for getting lists of computers. One is the whole list, the other is a given person's computers:
api.route(ComputerList, 'computer_list', '/computers', '/persons/<int:id>/computers')
The /computers route seems to work as I'd expect, but the latter one does not. It appears you are overriding query in the example, which the docs don't really go into detail about, and it doesn't seem to make use the before_get call.
Without thinking about how the library works, it seems like it'd be great if I had a hook somewhere to return the value of:
self.session.query(Person).filter_by(id=view_kwargs['id']).one().computers
and just return the computers object off of my SQLAlchemy model since that was already set up.
Several questions then:
- Is what is in the example considered best practice for pulling down a resources (e.g persons) items (e.g computers)?
- What if the person had 2 different sets of computers (e.g old and new). Is the best practice then in the
ComputerListobject to branch based off theview_kwargsto decide how to filter the list? - How do I make use of existing SQLAlchemy connections?
- What is the main use case for using
before_get_object(and other variants)? In a typical setup, what is it intended to manipulate before the rest of the method runs?
- I don't understand the first question => you want to pull a resource or several resources ?
- Yes you're right I usually use view_kwargs to switch between cases
- https://flask-rest-jsonapi.readthedocs.io/en/latest/data_layer.html with the data_layer config you can specify the session or create a session from a connection
- You can only manipulate view_kwargs, this hook is setup up for homogeneity
It is possible to define two view classes, one without the query method and one with the query method. The class without the query method would then serve /computers, and the class with the query (which may inherit from the one without the query) would serve /persons/:id/computers. The query method then also does not have to check whether the id is passed, because it always is. This would look like:
class ComputerList(ResourceList):
schema = ComputerSchema
data_layer = {'session': db.session, 'model': Computer}
class PersonComputerList(ComputerList)
def query(self, view_kwargs):
try:
self.session.query(Person).filter_by(id=view_kwargs['id']).one()
except NoResultFound:
raise ObjectNotFound({'parameter': 'id'}, "Person: {} not found".format(view_kwargs['id']))
else:
return self.session.query(Computer).join(Person).filter(Person.id == view_kwargs['id'])
def before_create_object(self, data, view_kwargs):
person = self.session.query(Person).filter_by(id=view_kwargs['id']).one()
data['person_id'] = person.id
view_kwargs = True
data_layer = {'session': db.session,
'model': Computer,
'methods': {'query': query,
'before_create_object': before_create_object}}
...
api.route(ComputerList, 'computer_list', '/computers')
api.route(PersonComputerList, 'person_computer_list', '/persons/<int:id>/computers')
I think this provides a nicer separation of concerns, and keeps things more flexible e.g. in the case described in question 2.
Note the view_kwargs = True class attribute on PersonComputerList which is necessary to prevent Werkzeug router errors (although I wasted some time reading over it, it is in fact documented).