代码之家  ›  专栏  ›  技术社区  ›  Attack68

基于排除关系中的项的Sqlalchemy查询

  •  2
  • Attack68  · 技术社区  · 7 年前

    我有一张桌子 PartyOrganiser (s) ,还有一张桌子 Contact (s) 和一张桌子 Party (s) 是的。

    聚会 一对多 .
    合作者 联系人
    聚会 联系人 多对多 ,具有关联表。

    class PartyOrganiser(db.Model):
        __tablename__ = 'party_organiser'
        id   = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String)
        parties  = db.relationship("Party",    back_populates='party_organiser')
        contacts = db.relationship("Contacts", back_populates='party_organiser')
    
    contact_party_ass_tab = db.Table('contact_party', db.Model.metadata,
        db.Column('party_id', db.Integer, db.ForeignKey('party.id')),
        db.Column('contact_id', db.Integer, db.ForeignKey('contact.id')))
    
    class Party(db.Model):
        __tablename__ = 'party'
        id      = db.Column(db.Integer, primary_key=True)
        details = db.Column(db.String)
        party_organiser_id = db.Column(db.Integer, db.ForeignKey('party_organiser.id'), nullable=False)
        party_organiser = db.relationship("PartyOrganiser", back_populates="parties", uselist=False)
        attendees = db.relationship("Contact", secondary=contact_party_ass_tab, back_populates='intended_parties')
    
    class Contact(db.Model):
        __tablename__ = 'contact'
        id   = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String)
        party_organiser_id = db.Column(db.Integer, db.ForeignKey('party_organiser.id'), nullable=False)
        party_organiser = db.relationship("PartyOrganiser", back_populates="parties", uselist=False)
        intended_parties = db.relationship("Contact", secondary=contact_party_ass_tab, back_populates='attendees')
    

    主要问题:

    从语法上讲,对于一个特定的聚会,我想获得一个与聚会组织者相关的、尚未参加聚会的联系人的列表。也就是说,称他们为潜在的\u与会者,我希望以下是一个SQLalchemy查询风格的解决方案:

    class Party(db.model):
        ...
        @property
        def potential_attendees(self):
            # get all contacts for the right party_organiser
            sub_query = Contact.query.filter(Contact.party_organiser_id == self.party_organiser_id)
            # then eliminate those that are already attendees to this party..
            return sub_query.difference(self.attendees) # <- pseudocode
    

    子问题:

    合作者 聚会 联系人 :只有共享聚会组织者的聚会和与会者才能关联。也就是说,PartyOrganiser1的联系人都不能参加PartyOrganiser2组织的Party2。我并不清楚,这是按照上述格式的要求进行约束的。事实上我认为不是。如何实现此约束?

    1 回复  |  直到 7 年前
        1
  •  1
  •   suryasankar    7 年前

    您可以使用 NOT EXISTS 在联接表上构造。

    @property
    def potential_attendees(self):
        sq = db.session.query(contact_party_ass_tab.columns.contact_id).subquery()
        return db.session.query(Contact).filter(
                Contact.party_organiser_id==self.party_organiser_id,
                ~exists().where(sq.c.contact_id==Contact.id)
            ).all()
    

    from flask import Flask
    from flask.ext.sqlalchemy import SQLAlchemy
    from sqlalchemy import exists
    
    from sqlalchemy.orm import validates
    
    
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
    db = SQLAlchemy(app)
    
    class PartyOrganiser(db.Model):
        __tablename__ = 'party_organiser'
        id   = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String)
        parties  = db.relationship("Party",    back_populates='party_organiser')
        contacts = db.relationship("Contact", back_populates='party_organiser')
    
        def __repr__(self):
            return self.name
    
    
    contact_party_ass_tab = db.Table('contact_party', db.Model.metadata,
        db.Column('party_id', db.Integer, db.ForeignKey('party.id')),
        db.Column('contact_id', db.Integer, db.ForeignKey('contact.id')))
    
    class Party(db.Model):
        __tablename__ = 'party'
        id      = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String)
        details = db.Column(db.String)
        party_organiser_id = db.Column(db.Integer, db.ForeignKey('party_organiser.id'), nullable=False)
        party_organiser = db.relationship("PartyOrganiser", back_populates="parties", uselist=False)
        attendees = db.relationship("Contact", secondary=contact_party_ass_tab, back_populates='intended_parties')
    
        def __repr__(self):
            return self.name
    
        @property
        def potential_attendees(self):
            sq = db.session.query(contact_party_ass_tab.columns.contact_id).subquery()
            return db.session.query(Contact).filter(
                    Contact.party_organiser_id==self.party_organiser_id,
                    ~exists().where(sq.c.contact_id==Contact.id)
                ).all()
    
        @validates('attendees')
        def validate_attendee(self, key, attendee):
            assert attendee.party_organiser_id == self.party_organiser_id
            return attendee
    
    class Contact(db.Model):
        __tablename__ = 'contact'
        id   = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String)
        party_organiser_id = db.Column(db.Integer, db.ForeignKey('party_organiser.id'), nullable=False)
        party_organiser = db.relationship("PartyOrganiser", back_populates="contacts", uselist=False)
        intended_parties = db.relationship("Party", secondary=contact_party_ass_tab, back_populates='attendees')
    
        def __repr__(self):
            return self.name
    
        @validates('intended_parties')
        def validate_party(self, key, party):
            assert party.party_organiser_id == self.party_organiser_id
            return party
    
    db.create_all()
    
    organiser1 = PartyOrganiser(name="organiser1")
    organiser2 = PartyOrganiser(name="organiser2")
    
    db.session.add_all([organiser1, organiser2])
    db.session.commit()
    
    
    org1_party1 = Party(name="Organiser1's Party1", party_organiser_id=organiser1.id)
    org1_party2 = Party(name="Organiser1's Party2", party_organiser_id=organiser1.id)
    
    org2_party1 = Party(name="Organiser2's Party1", party_organiser_id=organiser2.id)
    org2_party2 = Party(name="Organiser2's Party2", party_organiser_id=organiser2.id)
    
    db.session.add_all([org1_party1, org1_party2, org2_party1, org2_party2])
    db.session.commit()
    
    org1_contact1 = Contact(name="Organiser1's contact 1", party_organiser_id=organiser1.id)
    org1_contact2 = Contact(name="Organiser1's contact 2", party_organiser_id=organiser1.id)
    org1_contact3 = Contact(name="Organiser1's contact 3", party_organiser_id=organiser1.id)
    org1_contact4 = Contact(name="Organiser1's contact 4", party_organiser_id=organiser1.id)
    
    org2_contact1 = Contact(name="Organiser2's contact 1", party_organiser_id=organiser2.id)
    org2_contact2 = Contact(name="Organiser2's contact 2", party_organiser_id=organiser2.id)
    org2_contact3 = Contact(name="Organiser2's contact 3", party_organiser_id=organiser2.id)
    org2_contact4 = Contact(name="Organiser2's contact 4", party_organiser_id=organiser2.id)
    
    db.session.add_all([org1_contact1, org1_contact2, org1_contact3, org1_contact4, org2_contact1, org2_contact2, org2_contact3, org2_contact4])
    db.session.commit()
    
    org1_party1.attendees.append(org1_contact1)
    db.session.commit()
    
    print "Potential attendees of org1_party1 ", org1_party1.potential_attendees
    
    print "Attempting to add a contact of a different organiser. Will throw exception"
    
    org1_party1.attendees.append(org2_contact1)
    

    In [1]: from exclusion_query import *
    /home/surya/Envs/inkmonk/local/lib/python2.7/site-packages/flask_sqlalchemy/__init__.py:794: FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True or False to suppress this warning.
      'SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and '
    Potential attendees of org1_party1  [Organiser1's contact 2, Organiser1's contact 3, Organiser1's contact 4]
    Attempting to add a contact of a different organiser. Will throw exception
    ---------------------------------------------------------------------------
    AssertionError                            Traceback (most recent call last)
    <ipython-input-1-4380704ace46> in <module>()
    ----> 1 from exclusion_query import *
    
    推荐文章