1. 程式人生 > >使用sqlalchemy的ORM創建外鍵關聯時報錯

使用sqlalchemy的ORM創建外鍵關聯時報錯

自增 alc 單點 odi eat nullable 錯誤 現在 admin

在學習使用sqlalchemy模塊的時候踩了一個坑,分享一下。

埋下隱患

我先用下面的語句創建了一張學生信息表:

> CREATE TABLE student (
    -> id INT UNSIGNED AUTO_INCREMENT,
    -> name VARCHAR(20) NOT NULL,
    -> age TINYINT,
    -> PRIMARY KEY (id)
    -> );

表裏就3個字段:自增id(無符號的數字,自增id不會是負數,當然用無符號,感覺自己好專業),name 和 age(話說這個也應該是無符號)。

出現報錯

在學習了mysql之後,學習了一下PyMySql 模塊。最後是這個ORM,SQLAchemy。用這個表測試了很多命令和知識點,然後進行到使用SQLAlchemy建立外鍵關聯的時候卡住了。之前也有SQL語句建過外鍵關聯,也很順利。
現在已有一張學生信息表,再創建一張學生成績表。3個字段:考試名稱、學生id、成績。考試名和學生id作為復合主鍵,學生id關聯信息表中的id,做外鍵關聯:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy import ForeignKey  # 外鍵關聯需要這個

engine = create_engine("mysql+pymysql://admin:[email protected]/week12",
                       encoding=‘utf-8‘, echo=True)
Base = declarative_base()  # 生成orm基類

class Student(Base):
    __tablename__ = ‘student‘  # 表名,這張表不創建,可以寫的簡單點
    id = Column(primary_key=True)  # 只要聲明你需要的字段名,主鍵必須聲明
    name = Column()  # 字段類型可以不要,我們不是創建表
    age = Column()

class Exam(Base):
    __tablename__ = ‘exam‘
    name = Column(String(32), primary_key=True)
    student_id = Column(Integer(), ForeignKey("student.id"), primary_key=True)  # 聲明外鍵關聯
    score = Column(Integer, nullable=False)  # 規定不能為空

Base.metadata.create_all(engine)  # 創建表

讓後,報錯了。開啟echo,首先是SQL語句能正常生成了:

CREATE TABLE exam2 (
    name VARCHAR(32) NOT NULL, 
    student_id INTEGER NOT NULL, 
    score INTEGER NOT NULL, 
    PRIMARY KEY (name, student_id), 
    FOREIGN KEY(student_id) REFERENCES student (id)
)

之後是長長的報錯內容,就看最後一行:

sqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1005, "Can‘t create table ‘week12.exam2‘ (errno: 150)") [SQL: ‘\nCREATE TABLE exam2 (\n\tname VARCHAR(32) NOT NULL, \n\tstudent_id INTEGER NOT NULL, \n\tscore INTEGER NOT NULL, \n\tPRIMARY KEY (name, student_id), \n\tFOREIGN KEY(student_id) REFERENCES student (id)\n)\n\n‘] (Background on this error at: http://sqlalche.me/e/2j85)

初步排查

首先想到的是代碼寫錯了,仔細檢查。全是抄老師博客上的代碼,還仔細看了視頻,仔細核對。發現沒寫錯什麽。
賬號權限肯定不會有問題,因為之前已經用這個賬號通過python試了很多事情了,包括創建表。
這裏使用了復合主鍵,和例子稍微有點區別,於是改成了簡單的單一主鍵,也不行。
憑自己的能力就只能做這點了,然後上網搜。先把錯誤信息開頭的內容,就是這些:“sqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1005, "Can‘t create table“ 去網上搜索了一番。並沒有太明確的發現。
最後智能的搜索引擎幫我提煉出了一組關鍵字,其中有一個是“mysql error 1005”。之前一直以為是python的報錯,現在想想可能是mysql的報錯,於是用這個搜索了一下。

跳進新坑

這次的關鍵字提煉比較準確,找到的內容看上去都比較有用。
首先找到的是這個原因:“外鍵重復,刪除該表外鍵“。一想,之前用SQL語句練習外鍵的時候已經關聯了一個表了,這次要拿另外一個表關聯這個鍵,是不是就關聯不上了。我就平時一個只用過幾個SELECT命令的新手,沒理解那麽深啊。於是一頭跳進這個坑又轉悠了一番,最後還算好被我跳出來了。

終於醒悟

繼續看看還有什麽原因:“外鍵字段與要做外鍵校驗的字段類型不匹配 ”。處於對自己的不自信,又去仔細看了下自己的代碼,看看類型有沒有寫,有沒聲明。原表的類型因為不用創建,都被我缺省了,於是試著寫全,有沒用。
然後去SQL裏用DESC命令看看字段類型,SQL裏是這樣的:
技術分享圖片
一看自己的代碼裏Integer()裏面沒聲明長度,於是加上。不過pycharm顯示有錯誤。不管,試一下,當然還是不行。
雖然這個點是錯誤的,但是成功的讓我註意到這個我前一天創建的表裏的這個unsigned標誌。於是試著在代碼裏聲明為這個字段聲明是無符號的。博客是視頻裏都沒有,自己嘗試性的加在括號裏,加在外層的Column括號裏都不行後。最後還是只能上網搜。
這次目標很明確,就是 "sqlalchemy 無符號數字"。目標很明確,不過沒人專門講這個問題。打開網頁使用Ctrl+F查找終於找到了用法:

from sqlalchemy.dialects.mysql import INTEGER
id = Column(INTEGER(unsigned=True), primary_key=True)

跳出火坑

找對了問題,最後自然就迎刃而解了。要創建表的代碼如下:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy import ForeignKey  # 外鍵關聯需要這個
from sqlalchemy.dialects.mysql import INTEGER  # 要使用無符號的整數

engine = create_engine("mysql+pymysql://admin:[email protected]/week12",
                       encoding=‘utf-8‘, echo=True)
Base = declarative_base()  # 生成orm基類

class Student(Base):
    __tablename__ = ‘student‘  # 表名,這張表不創建,可以寫的簡單點
    id = Column(primary_key=True)  # 只要聲明你需要的字段名,主鍵必須聲明
    name = Column()  # 字段類型可以不要,我們不是創建表
    age = Column()

class Exam(Base):
    __tablename__ = ‘exam‘
    name = Column(String(32), primary_key=True)
    student_id = Column(INTEGER(unsigned=True), ForeignKey("student.id"), primary_key=True)  # 聲明外鍵關聯
    score = Column(Integer, nullable=False)  # 規定不能為空
Base.metadata.create_all(engine)  # 創建表

難怪講課演示的好好的,我抄來自己試就不對。我一般嫌用的例子不好,自己會改一改(演示的例子中用的都是標準的int類型)。然後就會出這種幺蛾子。不過還好,自己能爬出來。

使用sqlalchemy的ORM創建外鍵關聯時報錯