flask-rest-jsonapi icon indicating copy to clipboard operation
flask-rest-jsonapi copied to clipboard

Possible to get outdated info using GET after PATCH when using after_patch

Open graysonhead opened this issue 6 years ago • 2 comments
trafficstars

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>')

graysonhead avatar Nov 24 '18 02:11 graysonhead

In my opinion you should:

  • use after_update_object function instead of after_patch, by defining a "methods" dictionnary in your data layer. after_update_object has 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)?

etiennecaldo avatar Dec 07 '18 06:12 etiennecaldo

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.

graysonhead avatar Dec 25 '18 05:12 graysonhead