那些年我們學Flask-SQLAlchemy,實現數據庫操作,分頁等功能

那些年我們學Flask-SQLAlchemy

實現數據庫操作,分頁等功能 



Flask-SQLAlchemy庫讓flask更方便的使用SQLALchemy,是一個強大的關係形數據庫框架,既可以使用orm方式操作數據庫,也可以使用原始的SQL命令.

Flask-Migrate 是一個數據遷移框架,需要通過Flask-script庫來操作.



一.配置Flask-SQLAlchemy

程序使用的數據庫地址需要配置在SQLALCHEMY_DATABASE_URI中,SQLALchemy支持多種數據庫,配置格式如下:

  Postgres:

  postgresql://scott:tiger@localhost/mydatabase

  MySQL:

  mysql://scott:tiger@localhost/mydatabase

  Oracle:

  oracle://scott:[email protected]:1521/sidname

  SQLite:

  sqlite:////absolute/path/to/foo.db

db是SQLALchemy類的實例,表示程序使用的數據庫,爲用戶提供Flask-SQLALchemy的所有功能

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
#配置數據庫地址
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://username:password@localhost:3306/DB_name?charset=utf8'
#該配置爲True,則每次請求結束都會自動commit數據庫的變動
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
db = SQLAlchemy(app)
#也可以db = SQLAlchemy()      db.init_app(app)

二.定義模型

Flask-SQLALchemy使用繼承至db.Model的類來定義模型,如:

class User(db.Model, UserMixin):#UserMixin是Flask-Login庫中所需要的
    __tablename__ = 'users'
    #每個屬性定義一個字段
    id = db.Column(db.Integer,primary_key=True)
    username = db.Column(db.String(64),unique=True)
    password = db.Column(db.String(64))

    #定製顯示的格式    
    def __repr__(self):        
        return '<User %r>' % self.username


定義完需要在Python Shell中導入db,調用db.create_all()來創建數據庫

(1)常用字段選項:

  primary_key 設置主鍵

  unique 是否唯一

  index 是否創建索引

  nullable 是否允許爲空

  default 設置默認值,可以傳入函數的引用 如傳入 datetime.datetime.utcnow 則每次創建時時間都是最新時間

三.增刪查改

(1) 插入數據:

from app.models import User
from app import db

#創建一個新用戶
u = User()
u.username = 'abc'
u.password = 'abc'

#將用戶添加到數據庫會話中
db.session.add(u)

#將數據庫會話中的變動提交到數據庫中,如果不Commit,數據庫中是沒有改動的
db.session.commit()

(2)查找數據:

#返回所有用戶保存到list中
user_list = User.query.all()

#查找username爲abc的第一個用戶,返回用戶實例
u = User.query.filter_by(username='abc').first()

#模糊查找用戶名以c結尾的所有用戶
user_list  = User.query.filter(username.endswith('c')).all()

#查找用戶名不是abc的用戶
u = User.query.filter(username != 'abc').first()


(3)刪除數據:

user = User.query.first()
db.session.delete(user)
db.session.commit()


(4)修改數據:

u = User.query.first()
u.username = 'Mackie'
db.session.commit()


四.一對多關係

我的理解是:在多的一邊定義外鍵,而relathonship()函數是用來建立關係的,可以只在一邊定義,也可以兩邊都使用(只在一邊使用時加上了backref選項等同於兩邊都使用)

class Person(db.Model):
        __tablename__ = 'persons'
        
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))    
    #backref將在Address表中創建個名爲persons的Person引用,之後可以使用address.persons         #訪問這個地址的所有人
    addresses = db.relationship('Address', backref='persons',lazy='dynamic')

class Address(db.Model):
        __tablename__ = 'address'
        
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(50))    
    #在多的一邊使用db.ForeignKey聲明外鍵
    person_id = db.Column(db.Integer, db.ForeignKey('person.id'))


五.多對多關係

多對多關係可以分解爲原表和關聯表之間兩個多對一關係,如下代碼建立了學生與所選課程之間的關係:

#創建關聯表,兩個字段的外鍵是另兩個表,一個學生對應多個關聯表,一個關聯表對應多個課程
registrations = db.Table('registrations',
                         db.Column('student_id',db.Integer,db.ForeignKey('students.id')),
                         db.Column('class_id',db.Integer,db.ForeignKey('classes.id'))
                         )

class Student(db.Model):
    __tablename__ = 'students'
    id = db.Column(db.Integer,primary_key=True,)
    name = db.Column(db.String)
    classes = db.relationship('Class',
                              secondary = registrations, #關聯表,只需要在一個表建立關係,sqlalchemy會負責處理好另一個表
                              backref = db.backref('students',lazy='dynamic'),
                              lazy = 'dynamic')


class Class(db.Model):
    __tablename__ = 'classes'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)


多對多的使用:

#學生1增加一門選課
student1.classes.append(class1)
#學生1退選class1
student1.classes.remove(class1)
#學生1所選課程,由於指定了lazy='dynamic'所以沒有直接返回列表,而需要使用.all()
student1.classes.all()


六.分頁導航

Flask-SQLALchemy的Pagination對象可以方便的進行分頁,

對一個查詢對象調用pagenate(page, per_page=20, error_out=True)函數可以得到pagination對象,第一個參數表示當前頁,第二個參數代表每頁顯示的數量,error_out=True的情況下如果指定頁沒有內容將出現404錯誤,否則返回空的列表

#從get方法中取得頁碼
page = request.args.get('page', 1, type = int)
#獲取pagination對象
    pagination = Post.query.order_by(Post.timestamp.desc()).paginate(page, per_page=10, error_out = False)

#pagination對象的items方法返回當前頁的內容列表
    posts = pagination.items


pagination對象常用方法:

has_next :是否還有下一頁

has_prev :是否還有上一頁

items : 返回當前頁的所有內容

next(error_out=False) : 返回下一頁的Pagination對象

prev(error_out=False) : 返回上一頁的Pagination對象

page : 當前頁的頁碼(從1開始)

pages : 總頁數

per_page : 每頁顯示的數量

prev_num : 上一頁頁碼數

next_num :下一頁頁碼數

query :返回 創建這個Pagination對象的查詢對象

total :查詢返回的記錄總數

iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2)

在模版中使用

方法一:

{% macro render_pagination(pagination, endpoint) %}
  <div class=pagination>
  {%- for page in pagination.iter_pages() %}
    {% if page %}
      {% if page != pagination.page %}
        <a href="{{ url_for(endpoint, page=page) }}">{{ page }}</a>
      {% else %}
        <strong>{{ page }}</strong>
      {% endif %}
    {% else %}
      <span class=ellipsis>…</span>
    {% endif %}
  {%- endfor %}
  </div>
{% endmacro %}


方法二:jinjia2渲染+bootstrap模板

<!-- 創建頁碼-->
<ul class="pagination">
{#上一頁#}
    {%  if pagination.has_prev  %}
        <li><a href="{{ url_for('UserAdmin',page=pagination.prev_num) }}">&laquo;</a></li>
    {% endif %}

    {#頁碼#}
    {% set page_now = pagination.page  %}
    {% set page_count = pagination.pages %}
    {% if pagination.pages <= 5 %}
        {% for p in pagination.iter_pages() %}
                {% if p == pagination.page %}
                  <li ><a style="background-color: darkgray;opacity: 0.7;color: black" href="{{ url_for('UserAdmin',page=p) }}">{{ p }}</a></li>
                {% else %}
                    <li ><a href="{{ url_for('UserAdmin',page=p) }}">{{ p }}</a></li>
                {% endif %}
        {% endfor %}


        {% else %}
            {%  if page_now-2 >0 %}
                <li><a href="{{ url_for('UserAdmin',page=page_now-2) }}">{{ page_now-2 }}</a></li>
            {% endif %}
            {% if  page_now-1 >0  %}
                <li><a href="{{ url_for('UserAdmin',page=page_now-1) }}">{{ page_now-1 }}</a></li>
            {% endif %}
                <li ><a style="background-color: darkgray;opacity: 0.7;color: black" href="{{ url_for('UserAdmin',page=page_now) }}">{{ page_now }}</a></li>
            {% if (page_count-page_now) >1  %}
                <li><a href="{{ url_for('UserAdmin',page=page_now+1) }}">{{ page_now+1 }}</a></li>
            {% endif %}
            {% if (page_count - page_now) >2 %}
                <li><a href="{{ url_for('UserAdmin',page=page_now+1) }}">{{ page_now+2 }}</a></li>
            {% endif %}
    {% endif %}

{#下一頁#}
    {%  if pagination.has_next  %}
        <li><a href="{{ url_for('UserAdmin',page=pagination.next_num) }}">&raquo;</a></li>
    {% endif %}

    <li><span style="color: black">頁數 ( {{ page_now }}/{{ page_count }} )</span></li>
</ul>


效果展示:

wKiom1gOOurSf9olAAAK6xH4iUE659.png

wKiom1gOOs-z9QPuAAAK6xH4iUE840.pngwKioL1gOOs-TWAuWAAAH7jdeSnQ274.png


常見的數據類型與配置



類型名稱python類型描述
Integerint常規整形,通常爲32位
SmallIntegerint短整形,通常爲16位
BigIntegerint或long精度不受限整形
Floatfloat浮點數
Numericdecimal.Decimal定點數
Stringstr可變長度字符串
Textstr可變長度字符串,適合大量文本
Unicodeunicode

可變長度Unicode字符串

Booleanbool布爾型
TIMESTAMP    timestamp          日期加時間類型
Datedatetime.date日期類型
Timedatetime.time時間類型
Intervaldatetime.timedelta時間間隔
Enumstr字符列表
PickleType任意Python對象自動Pickle序列化
LargeBinarystr二進制

常見的SQLALCHEMY列選項

可選參數描述
primary_key如果設置爲True,則爲該列表的主鍵
unique如果設置爲True,該列不允許相同值
index如果設置爲True,爲該列創建索引,查詢效率會更高
nullable如果設置爲True,該列允許爲空。如果設置爲False,該列不允許空值
default定義該列的默認值

關係選項:

常見的SQLALCHEMY列類型.配置選項和關係選項





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章