首页 > 代码库 > 阶段总结一:数据库篇

阶段总结一:数据库篇

<style>p,li { white-space: pre-wrap }</style>

最近一直在用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);
<style>p,li { white-space: pre-wrap }</style>

想对应的模型类如下:

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
<style>p,li { white-space: pre-wrap }</style>

使用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)
<style>p,li { white-space: pre-wrap }</style>

在使用中我发现,获取的结果中会比查询全表字段中多了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)
<style>p,li { white-space: pre-wrap }</style>

第二种:with_entities()方法

Students.query.with_entities(Students.name, Students.mobileno, Students.email)
<style>p,li { white-space: pre-wrap }</style>

但是:使用第一种方法是无法再使用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()
<style>p,li { white-space: pre-wrap }</style>

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>

阶段总结一:数据库篇