1. 程式人生 > >python學習筆記SQLAlchemy(八)

python學習筆記SQLAlchemy(八)

目的 不可 完整 nullable null username oot lena 註意

ORM 與 SQLAlchemy 簡介

ORM 全稱 Object Relational Mapping, 翻譯過來叫對象關系映射。簡單的說,ORM 將數據庫中的表與面向對象語言中的類建立了一種對應關系。這樣,我們要操作數據庫,數據庫中的表或者表中的一條記錄就可以直接通過操作類或者類實例來完成。

技術分享圖片

SQLAlchemy 是Python 社區最知名的 ORM 工具之一,為高效和高性能的數據庫訪問設計,實現了完整的企業級持久模型。

連接與創建

安裝SQLAlchemy:

cq@ubuntu:~$ sudo pip3 install sqlalchemy
The directory ‘/home/cq/.cache/pip/http‘ or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo‘s -H flag.
The directory ‘/home/cq/.cache/pip‘ or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo‘s -H flag.
Collecting sqlalchemy
  Downloading SQLAlchemy-1.2.2.tar.gz (5.5MB)
    100% |████████████████████████████████| 5.5MB 115kB/s 
Installing collected packages: sqlalchemy
  Running setup.py install for sqlalchemy ... done
Successfully installed sqlalchemy-1.2.2

另外,需要安裝一個 Python 與 MySQL 之間的驅動程序:

apt-get install python-mysqldb
pip3 install mysqlclient

連接數據庫
創建py文件寫入下面的內容:

#coding=utf-8
from sqlalchemy import create_engine
engine = create_engine(‘mysql+mysqldb://root:@localhost:3306/blog‘)
engine.execute(‘select * from user‘).fetchall()
print(engine)

在上面的程序中,我們連接了默認運行在 3306 端口的 MySQL 中的 blog 數據庫。

首先導入了 create_engine, 該方法用於創建 Engine 實例,傳遞給 create_engine 的參數定義了 MySQL 服務器的訪問地址,其格式為 mysql://<user>:<password>@<host>/<db_name>。接著通過 engine.execute 方法執行了一條 SQL 語句,查詢了 user 表中的所有用戶。

對象關系映射

要使用 ORM, 我們需要將數據表的結構用 ORM 的語言描述出來。SQLAlchmey 提供了一套 Declarative 系統來完成這個任務。我們以創建一個 users 表為例,看看它是怎麽用 SQLAlchemy 的語言來描述的:

#coding=utf-8
from sqlalchemy import create_engine,Column,String,Text,Integer
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine(‘mysql+mysqldb://root:@localhost:3306/blog‘)
Base = declarative_base()

class User(Base):
    __table__ = ‘user‘
    id = Column(Integer,primary_key=True)
    username = Column(String(64),nullable=False,index=True)
    password = Column(String(64),nullable=False)
    email = Column(String(64),nullable=False,index=True)

    def __repr__(self):
        return ‘%s(%r)‘ % (self.__class__.__name__,self.username)

Base.metadata.create_all(engine)

如果想使 Python 類映射到數據庫表中,需要基於 SQLAlchemy 的 declarative base class,也就是宣言基類創建類。當基於此基類,創建 Python 類時,就會自動映射到相應的數據庫表上。創建宣言基類,可以通過declarative_base 方法進行

from sqlalchemy.ext.declarative import declarative_base
engine = create_engine(‘mysql+mysqldb://root:@localhost:3306/blog‘)
Base = declarative_base()

在 User 類中,用 tablename 指定在 MySQL 中表的名字。我們創建了三個基本字段,類中的每一個 Column 代表數據庫中的一列,在 Colunm 中,指定該列的一些配置。第一個字段代表類的數據類型,上面我們使用 String, Integer 倆個最常用的類型,其他常用的包括:

  • Text
  • Boolean
  • SmallInteger
  • DateTime
    nullable=False 代表這一列不可以為空,index=True 表示在該列創建索引。
    另外定義 repr 是為了方便調試,你可以不定義,也可以定義的更詳細一些。
    運行程序,程序不會有輸出信息,但是 sqlalchemy 已經在 MySQL 數據庫裏面為我們創建了 users 表。
    此時 User 有一個 table 屬性,記錄了定義的表信息
In [1]: from sql import User

In [2]: User.__table__
Out[2]: Table(‘users‘, MetaData(bind=None), Column(‘id‘, Integer(), table=<users>, primary_key=True, nullable=False), Column(‘username‘, String(length=64), table=<users>, nullable=False), Column(‘password‘, String(length=64), table=<users>, nullable=False), Column(‘email‘, String(length=64), table=<users>, nullable=False), schema=None)

一對多關系

對於一個普通的博客應用來說,用戶和文章顯然是一個一對多的關系,一篇文章屬於一個用戶,一個用戶可以寫很多篇文章,那麽他們之間的關系可以這樣定義:

from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy import Column, String, Integer, Text
class User(Base):

    __tablename__ = ‘users‘

    id = Column(Integer, primary_key=True)
    username = Column(String(64), nullable=False, index=True)
    password = Column(String(64), nullable=False)
    email = Column(String(64), nullable=False, index=True)
    articles = relationship(‘Article‘)

    def __repr__(self):
        return ‘%s(%r)‘ % (self.__class__.__name__, self.username)

class Article(Base):

    __tablename__ = ‘articles‘

    id = Column(Integer, primary_key=True)
    title = Column(String(255), nullable=False, index=True)
    content = Column(Text)
    user_id = Column(Integer, ForeignKey(‘users.id‘))
    author = relationship(‘User‘)

    def __repr__(self):
        return ‘%s(%r)‘ % (self.__class__.__name__, self.title)

每篇文章有一個外鍵指向 users 表中的主鍵 id, 而在 User 中使用 SQLAlchemy 提供的 relationship 描述 關系。而用戶與文章的之間的這個關系是雙向的,所以我們看到上面的兩張表中都定義了 relationship。

創建的 articles 表有外鍵 userid, 在 SQLAlchemy 中可以使用 ForeignKey 設置外鍵。設置外鍵後,如果能夠直接從 articles 的實例上訪問到相應的 users 表中的記錄會非常方便,而這可以通過 relationship 實現。上面的代碼通過 relationship 定義了 author 屬性,這樣就可以直接通過 articles.author 獲取相應的用戶記錄。

SQLAlchemy 提供了 backref 讓我們可以只需要定義一個關系:
articles = relationship(‘Article‘, backref=‘author‘)
添加了這個就可以不用再在 Article 中定義 relationship 了!

一對一關系
在 User 中我們只定義了幾個必須的字段, 但通常用戶還有很多其他信息,但這些信息可能不是必須填寫的,我們可以把它們放到另一張 UserInfo 表中,這樣 User 和 UserInfo 就形成了一對一的關系。你可能會奇怪一對一關系為什麽不在一對多關系前面?那是因為一對一關系是基於一對多定義的:

class User(Base):

    __tablename__ = ‘users‘

    id = Column(Integer, primary_key=True)
    username = Column(String(64), nullable=False, index=True)
    password = Column(String(64), nullable=False)
    email = Column(String(64), nullable=False, index=True)
    articles = relationship(‘Article‘, backref=‘author‘)
    userinfo = relationship(‘UserInfo‘, backref=‘user‘, uselist=False)

    def __repr__(self):
        return ‘%s(%r)‘ % (self.__class__.__name__, self.username)

class UserInfo(Base):

    __tablename__ = ‘userinfos‘

    id = Column(Integer, primary_key=True)
    name = Column(String(64))
    qq = Column(String(11))
    phone = Column(String(11))
    link = Column(String(64))
    user_id = Column(Integer, ForeignKey(‘users.id‘))

定義方法和一對多相同,只是需要添加 uselist=False 。
需要註意的地方是定義 users 屬性時,使用了 relationship 的 backref 參數,該參數使得可以在 UserInfo 實例中,通過 userinfos.user 訪問關聯的所有用戶信息。

多對多關系
一遍博客通常有一個分類,好幾個標簽。標簽與博客之間就是一個多對多的關系。多對多關系不能直接定義,需要分解成倆個一對多的關系,為此,需要一張額外的表來協助完成,通常對於這種多對多關系的輔助表不會再去創建一個類,而是使用 sqlalchemy 的 Table 類:

# 在原來代碼的基礎上導入
from sqlalchemy import Table

article_tag = Table(
    # 第一個參數為表名稱,第二個參數是 metadata,這倆個是必須的,Base.metadata 是 sqlalchemy.schema.MetaData 對象,表示所有 Table 對象集合, create_all() 會觸發 CREATE TABLE 語句創建所有的表。
    ‘article_tag‘, Base.metadata,
    # 對於輔助表,一般存儲要關聯的倆個表的 id,並設置為外鍵
        #course_tag 是雙主鍵,雙主鍵的目的就是為了約束避免出現重復的一對主鍵記錄,大部分情況都是應用在這種多對多的中間表中。
    Column(‘article_id‘, Integer, ForeignKey(‘articles.id‘), primary_key=True),
    Column(‘tag_id‘, Integer, ForeignKey(‘tags.id‘), primary_key=True)
)

class Tag(Base):

    __tablename__ = ‘tags‘

    id = Column(Integer, primary_key=True)
    name = Column(String(64), nullable=False, index=True)
    articles = relationship(‘Articles‘,
                              secondary=article_tag,
                              backref=‘tages‘)
    #secondary 指的是中間表,backref 指向自己的這個表

    def __repr__(self):
        return ‘%s(%r)‘ % (self.__class__.__name__, self.name)

映射到數據
表已經描述好了,在文件末尾使用下面的命令在我們連接的數據庫中創建對應的表:

if __name__ == ‘__main__‘:
    Base.metadata.create_all(engine)

查看mysql:

mysql> show tables;
+----------------+
| Tables_in_blog |
+----------------+
| article_tag    |
| articles       |
| tags           |
| userinfos      |
| users          |
+----------------+
5 rows in set (0.00 sec)

python學習筆記SQLAlchemy(八)