swd*_*dev 1 python sqlalchemy flask flask-login
I am creating a Point of Sales application, with the typical data hierarchy : Company->branches->Sales->SaleData, this is the model definition (Note that, the User model already prepare and working as a flask-login compatible model) :
from flask_sqlalchemy import SQLAlchemy
from main import db
from collections import OrderedDict
class Users(db.Model,object):
'''
Adding object to trun sqlalchemy into json object
'''
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(60), unique=True)
firstname = db.Column(db.String(20))
lastname = db.Column(db.String(20))
password = db.Column(db.String)
email = db.Column(db.String(100), unique=True)
role = db.Column(db.String(20))
active = db.Column(db.Boolean)
company_id = db.Column(db.Integer, db.ForeignKey('companies.id'))
def __init__(self, username=None, password=None, email=None, firstname=None, lastname=None):
self.username = username
self.email = email
self.firstname = firstname
self.lastname = lastname
self.password = password
self.active = True
self.role = 'Admin'
def is_authenticated(self):
return True
def is_active(self):
return self.active
def is_anonymous(self):
return False
def get_id(self):
return unicode(self.id)
def _asdict(self):
'''
Thanks to http://stackoverflow.com/questions/7102754/jsonify-a-sqlalchemy-result-set-in-flask
'''
result = OrderedDict()
for key in self.__mapper__.c.keys():
result[key] = getattr(self, key)
return result
class Companies(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), unique=True)
address = db.Column(db.String)
users = db.relation('Users', backref=db.backref('users'))
token = db.Column(db.String) #for identification of client
branches = db.relationship("Branches")
def __init__(self, name=None, address=None, token=None):
self.name = name
self.address = address
self.token = token
class Branches(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), unique=True)
address = db.Column(db.String)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
token = db.Column(db.String) #for identification of client
company_id = db.Column(db.Integer, db.ForeignKey('companies.id'))
sales = db.relation('Sales',
backref=db.backref('sales', lazy='dynamic'),
cascade="all, delete-orphan",
lazy='dynamic',
passive_deletes=True)
def __init__(self, name=None, address=None, token=None, user_id=None):
self.name = name
self.address = address
self.token = token
self.user_id = user_id
class Sales(db.Model):
id = db.Column(db.Integer, primary_key=True)
day = db.Column(db.Date)
branch_id = db.Column(db.Integer, db.ForeignKey('branches.id'))
data = db.relationship("SaleData")
def __init__(self, day=None):
self.day = day
class SaleData(db.Model):
id = db.Column(db.Integer, primary_key=True)
sale_id = db.Column(db.Integer, db.ForeignKey('sales.id'))
cash_start_of_day = db.Column(db.Integer)
cash_end_of_day = db.Column(db.Integer)
income = db.Column(db.Integer) # which is end - start
def __init__(self, cash_start_of_day = None, cash_end_of_day = None, income = None):
self.cash_start_of_day = cash_start_of_day
self.cash_end_of_day = cash_end_of_day
self.income = income
Run Code Online (Sandbox Code Playgroud)
Now, if I try to add a Sales data to the branches, it didn't happen if I do this :
branch1 = company1.branches.filter().all()
Run Code Online (Sandbox Code Playgroud)
I can olny do this :
branch1 = company1.branches[0]
Run Code Online (Sandbox Code Playgroud)
If not using that [] operator, I got error message : AttributeError: 'InstrumentedList' object has no attribute'. I have already browse another answer here in SO, it got to do with that lazy things in backref definition, so I already modify my current model
但似乎我在这里遗漏了一些东西..任何线索?谢谢!
编辑1:添加了单元测试并添加了用户模型
我已经从Mark Hildreth得到了一个简明的答案,它为我节省了很多!因此,我将把这个模型的完整单元测试放在这里.我相信它会在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()
Run Code Online (Sandbox Code Playgroud)
在这个模型或单元测试中可能有不好的做法,如果你指出这一点我会很高兴:)
谢谢!
您可能希望查看文档的"收集配置"部分.有一些主要的内置方法来处理SQLAlchemy中关系的处理方式,文档的这一部分显示了各种方法.
默认情况下,当你有......
class Companies(db.Model):
...
branches = db.relationship("Branches")
...
Run Code Online (Sandbox Code Playgroud)
加载公司将加载所有分支(默认情况下,它将它们加载到列表中).因此,在检索公司后,company.branches返回一个分支列表.因为它是一个列表,所以它没有filter()or 等函数all().如果您不期望大量的分支列表,这可能是首选,因为您可能更有意义将分支用作列表而不是查询对象.将此作为列表允许您执行诸如...之类的操作
company = session.query(Companies).first()
my_branch = Branches(...)
company.branches.append(my_branch)
session.commit()
Run Code Online (Sandbox Code Playgroud)
这将正确创建新Branches对象,而无需将其专门添加到会话中(我认为非常漂亮).
作为旁注,如果你这样做type(company.branches),你就不会得到<type 'list'>,因为为了实现这个魔法,SQLAlchemy实际上将设置branches为一个像列表一样工作的对象类型,但实际上还有其他SQLAlchemy特定的信息.如果您没有猜到,此对象类型是您收到错误消息的"InstrumentedList".
但是,您可能不想这样做; 具体来说,这需要您一次加载所有分支,并且您可能只想一次加载几个,因为您有数千个(公司中有数千个分支,想象一下官僚......)
所以,你改变了关系,就像文档说的那样 ......
实现大型集合管理的关键特性是所谓的"动态"关系.这是一种可选的relationship()形式,它在访问时返回一个Query对象来代替集合.filter()标准可以显式应用,也可以通过数组切片应用限制和偏移:
看来这就是你想要做的事情,如果你想做的事情company.branches.filter(...).all().要做到这一点,你可以像文档所示,通过使lazy关系的属性"动态"...
class Companies(db.Model):
...
branches = db.relationship("Branches", lazy='dynamic')
...
Run Code Online (Sandbox Code Playgroud)
看起来你已经为分支机构做了这个 - >销售关系,但你没有为公司 - >分支机构关系,这就是给你错误的原因.
| 归档时间: |
|
| 查看次数: |
2941 次 |
| 最近记录: |