【问题标题】:sqlalchemy: keep column values from the association table in a many-to-many relationshipsqlalchemy:以多对多关系保持关联表中的列值
【发布时间】:2011-09-02 22:14:09
【问题描述】:

假设我有一个项目表和一个任务表。一个项目可能有许多任务,一个任务可能分配给多个项目。我有一个关联表project_task,里面有项目和任务的映射关系,还有一个额外的rate列,用来记录项目具体的任务率。

表:项目

  • 项目标识
  • 姓名
  • 说明

表:任务

  • 任务标识
  • 姓名
  • 说明
  • 利率

表:Project_Task

  • 项目标识
  • 任务标识
  • 费率

如何在 Sqlalchemy 中映射这种关系?我的目标是 project.tasks 应该给我一个与项目关联的任务对象列表,其中 task.rate 设置为 project_task 表中记录的速率。

非常感谢!

【问题讨论】:

    标签: many-to-many sqlalchemy


    【解决方案1】:

    在映射中使用表连接:

    import sqlalchemy
    
    from sqlalchemy import Column
    from sqlalchemy import Integer
    from sqlalchemy import DECIMAL
    from sqlalchemy import Unicode
    from sqlalchemy import Text
    from sqlalchemy import ForeignKey
    
    from sqlalchemy.sql import join
    
    from sqlalchemy.orm import relation
    from sqlalchemy.orm import column_property
    from sqlalchemy.orm import create_session
    
    from sqlalchemy.ext.declarative import declarative_base
    
    engine = sqlalchemy.create_engine('sqlite:///stackoverflow_6144557.db', echo = True)
    
    Base = declarative_base(bind=engine)
    
    class ProjectTask(Base):
        __tablename__ = 'project_task'
    
        projectid = Column(Integer, ForeignKey('project.projectid'), primary_key = True)
        taskid = Column(Integer, ForeignKey('task.taskid'), primary_key = True)
        project_rate = Column('rate', DECIMAL(12, 4))
    
    class Task(Base):
        __tablename__ = 'task'
    
        taskid = Column(Integer, primary_key = True)
        name = Column(Unicode(255))
        description = Column(Text)
        rate = Column(DECIMAL(12, 4))
    
    class Project(Base):
        __tablename__ = 'project'
    
        projectid = Column(Integer, primary_key = True)
        name = Column(Unicode(255))
        description = Column(Text)
        tasks = relation("ExtendedProjectTask", backref = "project", lazy = 'joined')
    
    class ExtendedProjectTask(Base):
        __table__ = join(ProjectTask.__table__, Task.__table__)
    
        projectid = column_property(ProjectTask.projectid)
        taskid = column_property(Task.taskid, ProjectTask.taskid)
        name = column_property(Task.name)
        description = column_property(Task.description)
        task_rate = column_property(Task.rate)
        project_rate = column_property(ProjectTask.project_rate)
    
        @property
        def rate(self):
            if self.project_rate is None:
                return self.task_rate
            else:
                return self.project_rate
    
    if __name__ == '__main__':
        Base.metadata.create_all(engine)
        session = create_session(engine)
        for project in session.query(Project).all():
            print "\n%r, %r, %r" % (project.projectid, project.name, project.description)
            for task in project.tasks:
                print "\t%r, %r, %r, %r" % (task.taskid, task.name, task.description, task.rate)
    

    【讨论】:

      【解决方案2】:

      这个怎么样?我正在将 ProjectTask 委托给 Task 以获取任务属性。我只设置了一个 get 属性,但您可以轻松地添加 set 和 delete(您可以很容易地想到一种通用的方法)。

      import sqlalchemy
      
      from sqlalchemy import Column
      from sqlalchemy import Integer
      from sqlalchemy import DECIMAL
      from sqlalchemy import Unicode
      from sqlalchemy import Text
      from sqlalchemy import ForeignKey
      
      from sqlalchemy.orm import relation
      from sqlalchemy.orm import create_session
      
      from sqlalchemy.ext.declarative import declarative_base
      
      engine = sqlalchemy.create_engine('sqlite:///stackoverflow_6144557.db')
      Base = declarative_base(bind=engine)
      
      class ProjectTask(Base):
          __tablename__ = 'project_task'
      
          projectid = Column(Integer, ForeignKey('project.projectid'), primary_key=True)
          taskid = Column(Integer, ForeignKey('task.taskid'), primary_key=True)
      
          project_rate = Column('rate', DECIMAL(12, 4))
          task = relation("Task", backref="project_tasks", lazy="joined")
      
          @property
          def name(self):
              return self.task.name
      
          @property
          def description(self):
              return self.task.description
      
          @property
          def rate(self):
              if self.project_rate is None:
                  return self.task.rate
              else:
                  return self.project_rate
      
      class Project(Base):
          __tablename__ = 'project'
      
          projectid = Column(Integer, primary_key=True)
          name = Column(Unicode(255))
          description = Column(Text)
      
          tasks = relation(ProjectTask, backref="project", lazy="joined")
      
      class Task(Base):
          __tablename__ = 'task'
      
          taskid = Column(Integer, primary_key=True)
          name = Column(Unicode(255))
          description = Column(Text)
          rate = Column(DECIMAL(12, 4))
      
      
      Base.metadata.create_all(engine)
      
      if __name__ == '__main__':
          session = create_session(engine)
      
          for project in session.query(Project).all():
              print "\n%r, %r, %r" % (project.projectid, project.name, project.description)
              for task in project.tasks:
                  print "\t%r, %r, %r, %r" % (task.taskid, task.name, task.description, task.rate)
      

      【讨论】:

      • 我认为连接表继承可以更优雅地处理委托。
      • 实际上,更值得尝试这里概述的方法:sqlalchemy.org/docs/06/orm/…。我现在正在上班的路上,但我稍后会试一试。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-05-25
      • 2015-03-16
      • 1970-01-01
      • 2018-11-24
      • 1970-01-01
      • 2016-04-04
      相关资源
      最近更新 更多