ManyToMany - circuitsacul/apgorm GitHub Wiki
Note
Nearly every decent ORM supports simple ManyToMany (SQLAlchemy, TortoiseORM, etc.). apgorm supports it to some extent, but it isn't as simple as making a field a ManyToManyField
that references another column. There are a few reasons for this:
- Requiring users to create the middle table allows control over the middle table (you can have more than just the FK fields)
- Allowing a simple
games = apgorm.ManyToMany(Game)
would make it difficult for the Game table to haveusers
without breaking static type checking - It would be difficult to support both methods; A simple ManyToMany and a more customizable one. If I were to switch to a simpler version, it would come at the cost of being able to control other aspects of it.
Usage
ManyToMany's can be used like this:
class User(apgorm.Model):
id_ = Serial().field()
games = apgorm.ManyToMany["Player", "Game"](
"id_", # the field on *this* model
"players.userid", # the field on the middle table that references users.id_
"players.gameid", # the field on the middle table that references games.id_
"games.id_", # the field on the final table that is reference by players
)
primary_key = (id_)
class Game(apgorm.Model):
id_ = Serial().field()
users = apgorm.ManyToMany["Player", "User"](
"id_", "players.gameid", "players.userid", "users.id_"
)
primary_key = (id_)
class Player(apgorm.Model): # this is the "middle table"
userid = Int().field()
gameid = Int().field()
uidfk = apgorm.ForeignKey(userid, User.id_) # although not *required*, this is highly recommended
gidfk = apgorm.ForeignKey(gameid, Game.id_) # ^^
primary_key = (userid, gameid,)
class Database(apgorm.Database):
users = User
games = Game
players = Player
Now, the ManyToMany "fields" can be used.
Fetch all of "Circuit"'s games:
circuit = await User.fetch(id_=0) # just pretend circuit's id is 0
circuits_games = await circuit.games.fetchmany()
Fetch all of the users in a game:
some_game = await Game.fetch() # just returns the first row found
users = await some_game.users.fectchmany()
Create a game and add Circuit to it:
new_game = await Game().create() # id_ is a Serial, so postgres gives it a default value
await circuit.games.add(new_game) # actually just creates a Player. In fact, this even returns the created player
Remove circuit from new_game
await new_game.users.remove(circuit)
Note: This is exactly the same as calling circuit.games.remove(new_game)
. This method returns list[Player], but if you use ForeignKeys properly that list will always contain exactly one Player.
There is also a working ManyToMany example under examples/
.