簡體   English   中英

Sqlalchemy:一對多關系與多對多關系相結合

[英]Sqlalchemy: One to Many relationship combined with Many to Many relationship

我有一個具有多對多關系的用戶和組表

_usergroup_table = db.Table('usergroup_table', db.metadata,
    db.Column('user_id',  db.Integer, db.ForeignKey('user.id')),
    db.Column('group_id', db.Integer, db.ForeignKey('group.id')))

class User(db.Model):
    """Handles the usernames, passwords and the login status"""
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(60), nullable=False, unique=True)

class Group(db.Model):
    """Used for unix-style access control."""
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(60), nullable=False)
    users = db.relationship('User', secondary=_usergroup_table,
                            backref='groups')

現在,我想向用戶類添加一個主要組。 當然,我可以只向group類添加一個group_id列和一個關系,但這有缺點。 我想在調用User.group時獲取所有組,包括primary_group。 主要組應始終是組關系的一部分。

編輯:

似乎要走的路是關聯對象

class User(db.Model, UserMixin):
    """Handles the usernames, passwords and the login status"""
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(60), nullable=False, unique=True)

    primary_group = db.relationship(UserGroup,
        primaryjoin="and_(User.id==UserGroup.user_id,UserGroup.primary==True)")

class Group(db.Model):
    """Used for unix-style access control."""
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(60), nullable=False)

class UserGroup(db.Model):
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    group_id = db.Column(db.Integer, db.ForeignKey('group.id'))
    active = db.Column(db.Boolean, default=False)

    user = db.relationship(User, backref='groups', primaryjoin=(user_id==User.id))
    group = db.relationship(Group, backref='users', primaryjoin=(group_id==Group.id))

我可以使用AssociationProxy簡化此操作,但是如何強制每個用戶僅使用一個主組?

如何使用GroupMemberships模型代替_usergroup_table來保持關聯? 用戶可以通過“組成員身份”擁有多個組,並且組成員身份可以包含其他屬性,例如給定的組是否是關聯用戶的主要組。

編輯

為了強制每個用戶限制一個主要組,我將在User模型中使用驗證,這樣分配記錄的任何一次嘗試分配比一個主要組更多(或更少)的嘗試都會導致錯誤。 我不知道完全依靠數據庫的完整性系統來實現相同結果的方法。 驗證檢查的編碼方式有很多種- 文檔顯示了使用validates()裝飾器的一種不錯的方法。

您最初想到的group_id方法在這里比“布爾標志”方法具有多個優點。

一方面,它自然受到約束,因此每個用戶只有一個主要組。 另外,加載user.primary_group意味着ORM可以通過其主鍵來標識此相關行,並且可以在身份映射中本地查找該行,或通過主鍵來發出簡單的SELECT,而不是發出具有硬性要求的查詢。 to-index WHERE子句,其中包含布爾值。 還有另一個問題是,無需進入關聯對象模式,該模式簡化了關聯表的使用,並使SQLAlchemy可以更有效地處理該表的負載和更新。

下面,我們使用事件,包括捕獲到“刪除”事件的@validates的新版本(自0.7.7起),以確保對User.groups和User.primary_group的對象級修改保持同步。 (如果使用的是0.7的較早版本,則可以使用屬性“ remove”事件或擴展屬性“ AttributeExtension.remove”(如果仍使用0.6或更早版本)。 如果要在數據庫級別實施此操作,則可以使用觸發器來驗證所需的完整性:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base= declarative_base()

_usergroup_table = Table('usergroup_table', Base.metadata,
    Column('user_id',  Integer, ForeignKey('user.id')),
    Column('group_id', Integer, ForeignKey('group.id')))

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    name = Column(String(60), nullable=False, unique=True)
    group_id = Column(Integer, ForeignKey('group.id'), nullable=False)
    primary_group = relationship("Group")

    @validates('primary_group')
    def _add_pg(self, key, target):
        self.groups.add(target)
        return target

    @validates('groups', include_removes=True)
    def _modify_groups(self, key, target, is_remove):
        if is_remove and target is self.primary_group:
            del self.primary_group
        return target

class Group(Base):
    __tablename__ = 'group'
    id = Column(Integer, primary_key=True)
    name = Column(String(60), nullable=False)
    users = relationship('User', secondary=_usergroup_table,
                            backref=backref('groups', collection_class=set))

e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)

s = Session(e)

g1, g2, g3 = Group(name='g1'), Group(name='g2'), Group(name='g3')
u1 = User(name='u1', primary_group=g1)

u1.groups.update([g2, g3])

s.add_all([
    g1, g2, g3, u1
])
s.commit()

u1.groups.remove(g1)
assert u1.primary_group is None
u1.primary_group = g2
s.commit()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM