您的位置:首页 > 数据库

使用sqlalchemy的ORM创建外键关联时报错

2018-01-12 19:56 597 查看
在学习使用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:admin123@192.168.246.12/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)[/code] 

初步排查

首先想到的是代码写错了,仔细检查。全是抄老师博客上的代码,还仔细看了视频,仔细核对。发现没写错什么。
账号权限肯定不会有问题,因为之前已经用这个账号通过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:admin123@192.168.246.12/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类型)。然后就会出这种幺蛾子。不过还好,自己能爬出来。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python orm