如何仅加载指定的列并仍然获取模型对象? (不推迟)

时间:2014-08-28 17:20:07

标签: python sqlalchemy

我想:

  1. 从多个表/模型中仅加载我想要的字段。
  2. 返回"模型对象"而不是我将把列传递给session.query的类似于对象的对象。这应该跨越关系,例如employee.company.name,其中company是Employee模型中的关系字段。
  3. 防止在初始选择查询后无意中加载其他字段。我可以使用defer.load_only('field_name')但是其他人可以访问我没有指定的模型属性,这将导致另一个查询运行。理想情况下,即使在模型中定义了该字段,访问查询中未指定的字段也会引发AttributeError
  4. 这是否可以使用SqlAlchemy提供的机制?这甚至是个好主意吗?

    我编写了以下功能,可以满足我的需求,但似乎其他人必须以更好,更标准的方式满足这些需求。

    class Attributable(object):
        pass
    
    
    def spread_result(row, columns):
        """
            :type row: sqlalchemy.util._collections.KeyedTuple
            :type columns: tuple
            Usage:
            >>> result = session.query(Model.field, AnotherModel.other_field).first()
            >>> obj = spread_result(result, ('field', 'another_model.other_field'))
            >>> obj.field
            'field_value'
            >>> obj.another_model.other_field
            'other_field_value'
            >>> obj.another_mapped_field
            AttributeError: 'Attributable' object has no attribute 'another_mapped_field'
        """
        root = Attributable()
        for column, value in zip(columns, row):
            obj = root
            parts = column.split('.')
            for i, attr in enumerate(parts):
                if i == len(parts) - 1:
                    setattr(obj, attr, value)
                else:
                    setattr(obj, attr, Attributable())
                    obj = getattr(obj, attr)
    
        return root
    

1 个答案:

答案 0 :(得分:1)

最简单的方法是创建一个" public"映射到同一个表但仅包含您希望可加载/可访问的列和其他属性的模型。

relevant documentation说:

  

include_properties或exclude_properties参数可以指定只应映射列的子集

如果您有一个Person模型,用户应该只看到id和名称," public" class会是这样的:

class PublicPerson(Base):
    __table__ = Person.__table__

    __mapper_args__ = {
        'include_properties': ['id', 'name']
    }

这是一个简单的可运行示例:

from datetime import datetime
from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session

engine = create_engine('sqlite://', echo=True)
session = Session(bind=engine)
Base = declarative_base(bind=engine)


class Person(Base):
    __tablename__ = 'person'

    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    birthday = Column(DateTime, nullable=False)


class PublicPerson(Base):
    __table__ = Person.__table__

    __mapper_args__ = {
        'include_properties': ['id', 'name']
    }


Base.metadata.create_all()

session.add(Person(name='Jan', birthday=datetime(2001, 1, 1)))

# query the full person, prints birthday
print(session.query(Person.birthday).scalar())

# query the "public" person, raises exception on birthday
print(session.query(PublicPerson.birthday).scalar())