首页 > 代码库 > 阶段总结一:数据库篇
阶段总结一:数据库篇
最近一直在用python的Flask框架开发应用。相比bottle框架,Flask丰富的扩展让代码写起来更加方便。在边踩坑边学习中得到了一些总结。比较杂,也算是定期的阶段总结吧,
一:初识Flask-SQLAlchmey
<style>p,li { white-space: pre-wrap }</style>假设有一个如下的数据表(数据库为pgsql)
--学生表 CREATE TABLE students ( id serial primary key, name varchar(20), mobileno varchar(11), email varchar(32), address varchar(100) ); --爱好表 CREATE TABLE hobby ( id serial primary key, students_id integer references students (id), name varchar(20) ); --朋友表 CREATE TABLE friends ( id serial primary key, frends_id integer references students (id), friended_id integer references students (id) ); --插入数据 INSERT INTO students (name, mobileno, email, address) VALUES (‘小王‘, ‘13511111111‘, ‘xiaowang@test.com‘, ‘北京‘),(‘小李‘, ‘13522222222‘, ‘xiaoli@test.com‘, ‘上海‘),(‘小张‘, ‘13533333333‘, ‘xiaozhang@test.com‘, ‘天津‘),(‘小徐‘, ‘13544444444‘, ‘xiaoxu@test.com‘, ‘河北‘); INSERT INTO hobby (students_id, name) VALUES (1, ‘唱歌‘),(1, ‘跳舞‘),(2, ‘跳舞‘),(2, ‘足球‘),(2, ‘篮球‘),(3, ‘唱歌‘),(3, ‘篮球‘),(3, ‘乒乓球‘),(4, ‘羽毛球‘),(4, ‘篮球‘),(4, ‘画画‘); INSERT INTO friends (frends_id, friended_id) VALUES (1, 2),(1, 3),(1, 4),(2, 1),(2, 3),(2, 4),(3, 1),(4, 1),(4, 2);
想对应的模型类如下:
class Friends(db.Model): __tablename__ = ‘friends‘ id = db.Column(db.Integer, primary_key=True) frends_id = db.Column(db.Integer, db.ForeignKey(‘students.id‘)) friended_id = db.Column(db.Integer, db.ForeignKey(‘students.id‘)) class Students(db.Model): __tablename__ = ‘students‘ id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(20)) mobileno = db.Column(db.String(11)) email = db.Column(db.String(32)) address = db.Column(db.String(100)) hobby = db.relationship(‘Hobby‘, backref=db.backref(‘students‘), lazy=‘dynamic‘) friended = db.relationship(‘Friends‘, foreign_keys=[Friends.frends_id], backref=db.backref(‘friends‘, lazy=‘joined‘), lazy=‘dynamic‘, cascade=‘all, delete-orphan‘) friends = db.relationship(‘Friends‘, foreign_keys=[Friends.friended_id], backref=db.backref(‘friended‘, lazy=‘joined‘), lazy=‘dynamic‘, cascade=‘all, delete-orphan‘) def __repr__(self): return ‘<Students %r>‘ % self.name class Hobby(db.Model): __tablename__ = ‘hobby‘ id = db.Column(db.Integer, primary_key=True) students_id = db.Column(db.Integer, db.ForeignKey(‘students.id‘)) name = db.Column(db.String(20)) def __repr__(self): return ‘<Hobby %r>‘ % self.name
使用Flask-SQLAlchmey处理时候,WEB没有问题,把结果集对象放到Jinja2模板中去解析,但是在给Android客户端返回数据时候便遇到了麻烦,参照了一些资料,发现大部分都是在表模型类中增加一个to_json的方法,如下
def to_json(demands): data = [{ "id": item.id, "content": item.content, "username": item.username, "city": item.city, "mobileno": hidmobile(item.mobileno), "tons": float(item.tons), "norm": item.norm, "faceurl": item.faceurl, "ctime": timestr(item.ctime) } for item in demands] return data
<style>p,li { white-space: pre-wrap }</style>
这样并不是很方便,因为这个方法可能在很多路由中被用到。如果是仅仅这一个表中的数据,通用行完全没有问题,但是对大型的数据处理来说,链表查询是经常会用到的。那么这里的字段便没法完全统一(因为可能会联不同的表,或者子查询).在搜索查阅时候发现一个方法(完美性有待进一步测试)
首先自定义一个json处理时候的类 class AlchemyEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj.__class__, DeclarativeMeta): fields = {} for jj in dir(obj): print jj for field in [x for x in dir(obj) if not x.startswith(‘_‘) and x != ‘metadata‘]: data = obj.__getattribute__(field) try: json.dumps(data) # this will fail on non-encodable values, like other classes fields[field] = data except TypeError: # 添加了对datetime的处理 if isinstance(data, datetime.datetime): fields[field] = datetime.datetime.strftime(data, ‘%Y-%m-%d %H:%M:%S‘) #data.isoformat() elif isinstance(data, datetime.date): fields[field] = datetime.datetime.strftime(data, ‘%Y-%m-%d‘) #data.isoformat() elif isinstance(data, datetime.timedelta): fields[field] = (datetime.datetime.min + data).time().isoformat() else: fields[field] = None # a json-encodable dict return fields return json.JSONEncoder.default(self, obj) #使用 student = Students.query.all() print student return json.dumps(student, cls=AlchemyEncoder)
在使用中我发现,获取的结果中会比查询全表字段中多了2个字段(query, query_class)。但是并不影响使用。如果你还有更好的方法,请指教下。谢谢~
<style>p,li { white-space: pre-wrap }</style>二:Flask-SQLAlchmey一些方法的使用
<style>p,li { white-space: pre-wrap }</style>1,查询数据表中具体的字段, 在查询优化上,只查询需要的字段可以优化查询速度
<style>p,li { white-space: pre-wrap }</style>第一种:query()方法
students = db.session.query(Students.name, Students.mobileno, Students.email)
第二种:with_entities()方法
Students.query.with_entities(Students.name, Students.mobileno, Students.email)
但是:使用第一种方法是无法再使用paginate()来分页查询的,因为paginate()方法只是Flask-SQLAlchemy的方法,而不是SQLAlchemy的方法,恰恰第一种方法使用的就是SQLAlchemy基类的方法
<style>p,li { white-space: pre-wrap }</style>2,执行原生sql语句
sql = "UPDATE students SET name=:name, address=:address WHERE id=:id" db.session.execute(sql, {"name": ‘王小明‘, "address": ‘北京丰台‘, "id":1}) db.session.commit()
<style>p,li { white-space: pre-wrap }</style>
当然SQLAlchemy的一些方法也是可用的,比如
from sqlalchemy import text db.session.query("id","name", "mobile" ).from_statement( text("SELECT id, name, mobileno AS mobile FROM students where name=:name") ).params(name=‘小张‘).all()
3,字段的别名
在联表查询时候,通常会给副表的字段设置一个别名,特别是主表和副表的字段相同时候,比如上面的hobby表和students表都有name字段,如果联表查询,就会出现问题,后来出现的字段会覆盖前面出现的字段.SQLAlchemy提供label()方法实现别名功能
data =http://www.mamicode.com/ Students.query.with_entities(Students.id, Students.name, Students.mobileno, Hobby.name.label(‘hobby_name‘)).join(Hobby).all()
for i in data: print i.name, i.hobby_name
<style>p,li { white-space: pre-wrap }</style>
4,postgresql的array_to_string的用法
这个相当于python的list.split()方法,但是却有妙用;
<style>p,li { white-space: pre-wrap }</style>场景:如上的students表和hobby表,一个学生有多个爱好(典型的一对多关系)。但是如何把这个学生和爱好合成一条记录展示呢,
首先想法就是取出所有的学生数据,然后循环,获取爱好数据,组合成一个字符串放到学生数据字典里。
但是使用array_to_string就可以使用一条sql语句搞定
select students.id, students.name, students.mobileno,array_to_string(array(SELECT name from hobby where hobby.students_id=students.id), ‘,‘) as hobbys from students
1 |
小李 |
13522222222 |
跳舞,足球,篮球 |
2 |
小张 |
13533333333 |
唱歌,篮球,乒乓球 |
3 |
小徐 |
13544444444 |
羽毛球,篮球,画画 |
4 |
王小明 |
13511111111 |
唱歌,跳舞 |
<style>p,li { white-space: pre-wrap }</style> <style>p,li { white-space: pre-wrap }</style> <style>p,li { white-space: pre-wrap }</style>
阶段总结一:数据库篇