Python 为了避免错误消息,SQLAlchemy的正确模型定义是什么:AttributeError:';仪表化列表';对象没有属性';?
我正在创建一个具有典型数据层次结构的销售点应用程序: 公司->分支机构->销售->销售数据,这是模型定义(注意,用户模型已经准备好并作为一个兼容的模型工作): 现在,如果我尝试向分支机构添加销售数据,如果我这样做,则不会发生:Python 为了避免错误消息,SQLAlchemy的正确模型定义是什么:AttributeError:';仪表化列表';对象没有属性';?,python,sqlalchemy,flask,flask-login,Python,Sqlalchemy,Flask,Flask Login,我正在创建一个具有典型数据层次结构的销售点应用程序: 公司->分支机构->销售->销售数据,这是模型定义(注意,用户模型已经准备好并作为一个兼容的模型工作): 现在,如果我尝试向分支机构添加销售数据,如果我这样做,则不会发生: branch1 = company1.branches.filter().all() branch1 = company1.branches[0] 我能做到这一点: branch1 = company1.branches.filter().all() bra
branch1 = company1.branches.filter().all()
branch1 = company1.branches[0]
我能做到这一点:
branch1 = company1.branches.filter().all()
branch1 = company1.branches[0]
如果不使用该[]
运算符,我会收到错误消息:AttributeError:“InstrumentedList”对象没有属性。我已经在这里浏览了另一个答案,它与backref定义中的惰性
有关,所以我已经修改了我当前的模型
但是我好像错过了什么。。有线索吗?
谢谢
编辑1:添加单元测试和用户模型
我已经从马克·希尔德雷思那里得到了一个简洁的答案,这为我节省了很多!因此,我将在这里对这个模型进行完整的单元测试。我相信这将帮助新手在SQLAlchemy中迈出第一步。下面是:
import unittest
from main import db
import models
import md5
import helper
class DbTest(unittest.TestCase):
def setUp(self):
db.drop_all()
db.create_all()
def test_user_and_company(self):
"""admin of a company"""
user1 = models.Users('eko', helper.hash_pass('rahasia'), 'swdev.bali@gmail.com')
db.session.add(user1)
db.session.commit()
"""the company"""
company1 = models.Companies('CDI','Glagah Kidul', 'empty')
db.session.add(company1)
company1.users.append(user1)
db.session.commit()
assert company1.users[0].id == user1.id
"""branches"""
company1.branches.append(models.Branches(name='Kopjar',address='Penjara Malaysia', token='empty token', user_id=user1.id))
company1.branches.append(models.Branches(name='Selangor',address='Koperasi Selangor', token='empty token', user_id=user1.id))
db.session.commit()
'''sales'''
branch1 = company1.branches.filter(models.Branches.name=='Kopjar').first()
assert branch1.name=='Kopjar' and branch1.company_id == company1.id
sale = models.Sales(day='2013-02-02')
sale.data.append(models.SaleData(cash_start_of_day = 0, cash_end_of_day = 500000, income = 500000))
branch1.sales.append(sale)
db.session.commit()
assert sale.id is not None
if __name__ == '__main__':
unittest.main()
在这个模型或单元测试中可能有不好的做法,如果您指出这一点,我将非常高兴:)
谢谢 您可能希望查看文档的这一部分。有几种主要的、内置的方法来处理SQLAlchemy中如何处理关系,文档的这一部分展示了各种方法 默认情况下,当您有
class Companies(db.Model):
...
branches = db.relationship("Branches")
...
加载公司将加载中的所有分支机构(默认情况下,它将它们加载到列表中)。因此,在检索公司后,company.branches
会向您返回分支机构列表。因为它是一个列表,所以它没有诸如filter()
或all()
之类的函数。如果您不希望看到大量分支列表,那么这可能是首选,因为将分支用作列表而不是查询对象可能更有意义。将此作为一个列表允许您执行以下操作
company = session.query(Companies).first()
my_branch = Branches(...)
company.branches.append(my_branch)
session.commit()
这将正确地创建新的分支
对象,而无需将其专门添加到会话中(我认为这非常漂亮)
作为旁注,如果您要执行类型(company.branchs)
,您将不会得到
,因为为了实现这一魔法,SQLAlchemy实际上会将分支
设置为一个类似于列表的对象类型,但实际上具有额外的SQLAlchemy特定信息。如果您没有猜到的话,这个对象类型就是您得到错误消息的“InstrumentedList”
但是,您可能不想这样做;具体来说,这要求您一次加载所有分支机构,您可能一次只想加载几个分支机构,因为您有数千个分支机构(一家公司中有数千个分支机构,想象一下官僚机构……)
所以,你改变关系
支持管理大型集合的一个关键特性是
所谓的“动态”关系。这是一种可选的
relationship(),它返回一个查询对象来代替集合
访问时。可以应用filter()标准以及限制和
偏移量,无论是显式偏移还是通过数组切片偏移:
如果您希望能够执行诸如company.branchs.filter(…).all()之类的操作,那么这就是您希望执行的操作。要做到这一点,您可以按照文档显示的那样,将关系的属性设置为lazy
“dynamic”
看起来您已经为“分支机构->销售”关系执行了此操作,但没有为“公司->分支机构”关系执行此操作,这就是导致错误的原因。您可能希望查看文档的部分。有几种主要的、内置的方法来处理SQLAlchemy中如何处理关系,文档的这一部分展示了各种方法
默认情况下,当您有
class Companies(db.Model):
...
branches = db.relationship("Branches")
...
加载公司将加载中的所有分支机构(默认情况下,它将它们加载到列表中)。因此,在检索公司后,company.branches
会向您返回分支机构列表。因为它是一个列表,所以它没有诸如filter()
或all()
之类的函数。如果您不希望看到大量分支列表,那么这可能是首选,因为将分支用作列表而不是查询对象可能更有意义。将此作为一个列表允许您执行以下操作
company = session.query(Companies).first()
my_branch = Branches(...)
company.branches.append(my_branch)
session.commit()
这将正确地创建新的分支
对象,而无需将其专门添加到会话中(我认为这非常漂亮)
作为旁注,如果您要执行类型(company.branchs)
,您将不会得到
,因为为了实现这一魔法,SQLAlchemy实际上会将分支
设置为一个类似于列表的对象类型,但实际上具有额外的SQLAlchemy特定信息。如果您没有猜到的话,这个对象类型就是您得到错误消息的“InstrumentedList”
但是,您可能不想这样做;具体来说,这要求您一次加载所有分支机构,您可能一次只想加载几个分支机构,因为您有数千个分支机构(一家公司中有数千个分支机构,想象一下官僚机构……)
所以,你改变关系
支持管理大型集合的一个关键特性是
所谓的“动态”关系。这是一种可选的
relationship(),它返回一个查询对象来代替集合
访问时。可以应用filter()标准以及限制和
偏移量,无论是显式偏移还是通过数组切片偏移:
如果您希望能够执行诸如company.branchs.filter(…).all()之类的操作,那么这就是您希望执行的操作。要做到这一点,您可以按照文档显示的那样,通过使la