flask-rest-jsonapi
flask-rest-jsonapi copied to clipboard
Possible to get outdated info using GET after PATCH when using after_patch
I have a resource called Segments which contains a geoJSON path and some statistical attributes (Only distance is included in this example for simplicity.)
The issue I'm running into is that when I use after_patch to re-calculate statistical info (distance of the selected path in this case), if I make a GET immediately after the PATCH the first GET has the old distance value, not the distance value that gets set in my after_patch function. If I wait long enough, the GET has the correct value, but for longer paths updating the distance/average speed/etc can be long and un-predictable. I would much rather have the request time out than get outdated information back.
I feel that I'm doing this the wrong way, but I wasn't able to figure out any cleaner way to do it, help appreciated!
Model:
class Segment(db.Model):
__tablename__ = 'segment'
id = Column(Integer, primary_key=True)
name = Column(String(120))
start_index = Column(Integer)
end_index = Column(Integer)
trip_id = Column(Integer, ForeignKey('trip.id'))
trip = relationship('Trip', back_populates='segments')
distance = Column(Integer)
path = Column(JSON)
def __init__(self, name, path_dict):
self.name = name
self.path = path_dict
self.reset_path_index()
def set_index(self, start, end):
self.start_index = start
self.end_index = end
self.calc_distance()
def reset_path_index(self):
self.start_index = 0
self.end_index = self.path['coordinates'].__len__()
self.calc_distance()
def calc_distance(self):
self.distance = calc_path_distance(self.path['coordinates'][self.start_index:self.end_index])
def __repr__(self):
return '<Segment: {}>'.format(self.name)``
Schema:
class SegmentSchema(Schema):
class Meta:
type_ = 'segment'
self_view = 'api.segment_detail'
self_view_kwargs = {'id': '<id>'}
self_view_many = 'api.segment_list'
id = fields.Str(as_string=True, dump_only=True)
name = fields.Str()
start_index = fields.Integer()
end_index = fields.Integer()
path = fields.Dict()
trip_id = fields.Int()
distance = fields.Int()
trip = Relationship(
attribute='trip',
self_view='api.segment_trip',
self_view_kwargs={'id': '<id>'},
related_view='api.trip_detail',
related_view_kwargs={'segment_id': '<id>'},
schema='TripSchema',
type='trip'
)
ResourceDetail:
class SegmentDetail(ResourceDetail):
schema = SegmentSchema
decorators = (login_required_api, )
data_layer = {
'session': db.session,
'model': models.Segment
}
def after_patch(*args, **kwargs):
session = args[0]._data_layer.session
segment = session.query(models.Segment).filter_by(id=args[1]['data']['id']).one()
segment.calc_distance()
session.add(segment)
session.commit()
api.route(SegmentDetail, 'segment_detail', '/segments/<int:id>')
In my opinion you should:
-
use
after_update_objectfunction instead of after_patch, by defining a "methods" dictionnary in your data layer.after_update_objecthas the obj you have changed as input argument, so no need to query it by yourself. -
Remove
session.add(segment). It is not necessary as the object is aldready created and you only update it. Just modify it and commit.
Moreover, the way you recover the session seems strange to me.
Why don't you use the db.session comming from your db object (the one you use to declare your Segment class)?
Thanks @etiennecaldo, It now looks like the follwing:
class SegmentDetail(ResourceDetail):
def after_update_object(self, object):
object.calc_distance()
self.session.add(object)
self.session.commit()
schema = SegmentSchema
decorators = (login_required_api, )
data_layer = {
'session': db.session,
'model': models.Segment,
'methods': {'after_update_object': after_update_object}
}
Which is much cleaner indeed. However, I seems like I can still get outdated information after I get a 200 from the PATCH request.