使用sqlalchemy的ORM創建外鍵關聯時報錯
埋下隱患
我先用下面的語句創建了一張學生信息表:
> 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創建外鍵關聯時報錯