I get a complex json every few hours. Part of it is a Game
object with a list of Player
objects that each Player
has a list of Training
object (each object has also other fields - ints, strings, list of strings etc.).
If the Game
object doesn't exist in my Postgres db (I check by the Game
's id field), I insert the whole structure to the db, each object as its own table (a table for Game
, a table for Player
and a table for Training
). Next time I get the json for this Game
, it already exists in the db, so I want to update it. I get the old json, the updated json, and the json_patch.
I wanted to query the db, convert it to json, and apply the patch on that json. The problem is that the lists (of the players for example) are not sorted in the same way as the lists in the updated_object
json. But I need to somehow work on the db because I need to have the primary keys of the objects so the ORM knows which objects to update.
What's the best way to approach it?
models:
class Game(Base):
__tablename__ = "game"
game_id: int = Column(INTEGER, primary_key=True,
server_default=Identity(always=True, start=1, increment=1, minvalue=1,
maxvalue=2147483647, cycle=False, cache=1),
autoincrement=True)
unique_id: str = Column(TEXT, nullable=False)
name: str = Column(TEXT, nullable=False)
players = relationship('Player', back_populates='game')
class Player(Base):
__tablename__ = "player"
player_id: int = Column(INTEGER, primary_key=True,
server_default=Identity(always=True, start=1, increment=1, minvalue=1,
maxvalue=2147483647, cycle=False, cache=1),
autoincrement=True)
unique_id: str = Column(TEXT, nullable=False)
game_id: int = Column(INTEGER, ForeignKey('game.game_id'), nullable=False)
name: str = Column(TEXT, nullable=False)
birth_date = Column(DateTime, nullable=False)
game = relationship('Game', back_populates='players')
trainings = relationship('Training', back_populates='player')
class Training(Base):
__tablename__ = "training"
training_id: int = Column(INTEGER, primary_key=True,
server_default=Identity(always=True, start=1, increment=1, minvalue=1,
maxvalue=2147483647, cycle=False, cache=1),
autoincrement=True)
unique_id: str = Column(TEXT, nullable=False)
name: str = Column(TEXT, nullable=False)
number_of_players: int = Column(INTEGER, nullable=False)
player_id: int = Column(INTEGER, ForeignKey('player.player_id'), nullable=False)
player = relationship('Player', back_populates='players')
json with updated data:
{"original_object":{"name":"Table Tennis","unique_id":"432","players":[{"unique_id":"793","name":"John","birth_date":"2023-10-28T00:10:56Z","trainings":[{"unique_id":"43","name":"Morning Session","number_of_players":3}, {"unique_id":"44","name":"Evening Session","number_of_players":2}]}]},"updated_object":{"name":"Table Tennis","unique_id":"432","players":[{"unique_id":"793","name":"John","birth_date":"2023-10-28T00:10:56Z","trainings":[{"unique_id":"43","name":"Morning Session","number_of_players":3}, {"unique_id":"44","name":"Evening Session","number_of_players":4}]}]},"json_patch":[{"op":"replace","path":"/players/0/trainings/1/numbre_of_players","value":4}],"timestamp":"2023-10-28T02:00:36Z"}
The json_patch updates the 'numbre_of_players' field of the second training to the value 4.
Code to add a new Game:
Session = sessionmaker(bind=engine_sync)
session = Session()
session.begin()
game = Game.from_dict(json['updated_object'])
existing_game = session.query(Game).filter_by(unique_id=game.id).first()
if not existing_game:
session.add(game)
session.commit()
But if the Game
does already exists in the db, I'm not sure what I should do.
This looks like a task for
sqlalchemy.orm.Session.merge()
. You're looking at 3 possible states of the sameGame
:You want to update/overwrite whatever the first two states are, assuming the third one is true.
No need to check if it's already in you session and/or in the db, because
merge()
checks this automatically, and if it doesn't find a matchingGame
, it adds it as a new one. From the doc:Remember to set up your cascades accordingly, to deal with all objects that might be related to it.