Many To Many Relationship Implementation - NFSandbox/sh_trade_backend GitHub Wiki

We use the Association Object pattern in SQLAlchemy to deal with Many-To-Many relationship.

Implementation

Check out SQLAlchemy - Association Object for more info.

Association Proxy

Association Proxy is used to map values across Many-To-Many tables.

Check out SQLAlchemy - Association Proxy for more info.

Lazy Load Of Association Proxy

Like normal relationship ORM attributes, the AssociationProxy columns are also designed as lazy-load attributes. The loading could happened in several points:

# consider we have a `User` class with `roles` as an AssociationProxy attr.

roles_of_this_user: ORMRole = user.roles    # Trigger lazy load

for r in roles_of_this_user:                # Trigger lazy load again!
    print(r)

From example above, we know:

  • Access AssociationProxy attribute itself will emit lazy querying.
  • If AssociationProxy could be iterate or accessed, then the access "inside" this attribute will also emit querying.

One way to make these attributes works in SQLAlchemy Asyncio pattern is to put all possible access to an AssociationProxy attributes inside AsyncSession.run_sync(). Bellowing is an example in production:

# member function of `User` class
def check_role(self, ss: AsyncSession, roles: List[str])
	roles_of_this_user: list[Role] = await ss.run_sync(lambda ss: self.roles)

	def _check_user_has_allowed_roles(ss):
	    # iterate through user's roles.
	    # once one of the user role is in the allowed role list, pass the test
	    for user_role in roles_of_this_user:
	        if user_role.role_name in roles:
	            return True

	    return False

	return await ss.run_sync(lambda ss: _check_user_has_allowed_roles(ss))

Soft Delete Of Relationship

All soft delete code implementation should follow with the Soft Deletion Guide of this project when deal with soft deletion of the Association ORM Table.

We may need to call session.refresh() after updating relationship and association to reflect the changes into ORM class instances.