2012-06-11 5 views
0

에 많은 함께 많은 관계로 하나는 내가 지금은 사용자에게 기본 그룹을 추가 할 많은 관계SQLAlchemy의 : 많은 관계

_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_id 열과 관계를 Group 클래스에 추가 할 수는 있지만 단점이 있습니다. primary_group을 포함하여 User.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 이것을 간단하게 할 수있는 연결 개체 것 같다,하지만 어떻게 내가 사용자 당 단 하나의 기본 그룹을 강요하는 걸까?

답변

2

원래 생각한 group_id 접근 방식은 여기 "부울 플래그"접근 방식에 대한 몇 가지 장점이 있습니다.

사용자 당 기본 그룹이 하나만 있도록 자연스럽게 제한됩니다. 또 다른 하나는 user.primary_group을 로딩하면 ORM이 기본 키로 관련 행을 식별 할 수 있고 해당 행을 식별 맵에서 로컬로 볼 수 있거나 하드 키가있는 쿼리를 내보내는 대신 기본 키로 간단한 SELECT를 내보낼 수 있음을 의미합니다. to-index WHERE 절은 내부에 부울 값이 있습니다. 또 하나의 이유는 관련 객체 패턴을 사용할 필요가 없기 때문에 연관 테이블의 사용을 단순화하고 SQLAlchemy가이 테이블에서 더 효율적으로로드 및 업데이트를 처리 할 수있게합니다.

이하에서는 "remove"이벤트를 포착하는 @validates의 새 버전 (0.7.7 현재)을 비롯하여 이벤트를 사용하여 User.groups 및 User.primary_group에 대한 개체 수준 수정 사항을 동기화 상태로 유지합니다. (0.7 이전 버전에서는 여전히 "0.6"또는 그 이전 버전 인 경우 속성 "remove"이벤트 또는 "AttributeExtension.remove"확장 메소드를 사용할 수 있습니다). DB 수준에서이를 적용하려면 찾고있는 무결성을 확인하기 위해 트리거를 사용할 수 있습니다.

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() 
2

_usergroup_table 대신 연결을 유지하는 GroupMemberships 모델은 어떻습니까? 사용자는 그룹 구성원을 통해 많은 그룹을 가질 수 있으며 그룹 구성원은 해당 그룹이 연관된 사용자의 기본 그룹인지 여부와 같은 추가 속성을 보유 할 수 있습니다.

편집

사용자 당 하나 개의 기본 그룹의 제한을 집행하기 위해, 내가, 사용자 모델에서 유효성 검사를 사용하는 것이 그러한 시도가 초래 한 주요 군에 비해 이상 (이하)을 할당하는 것을 레코드가 저장 될 때의 오류. 나는 순수하게 데이터베이스의 무결성 시스템에 의존하는 동일한 결과를 달성하는 방법을 알고 있지 않습니다. 유효성 검사를 코딩하는 방법에는 여러 가지가 있습니다. documentation은 validates() 데코레이터를 사용하는 좋은 접근법을 보여줍니다.

+0

docs.sqlalchemy.org/ko/rel_0_7/orm/relationships.html#association-pattern) – urschrei

+0

@urschrei 편집보기 – P3trus