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

在学习使用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类型)。然后就会出这种幺蛾子。不过还好,自己能爬出来。

原文地址:http://blog.51cto.com/steed/2060377

时间: 2024-08-15 16:21:34

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

《Entity Framework 6 Recipes》中文翻译系列 (29) ------ 第五章 加载实体和导航属性之过滤预先加载的实体集合和修改外键关联

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-13  过滤预先加载的实体集合 问题 你想过滤预先加载的实体集合,另外,你想使用Code-First来管理数据访问 解决方案 实体框架不支持直接使用Include()时过滤关联实体集合,但我们可以通过创建一个匿名类型来完成同样的事情,匿名类型包含实体和要过滤的关联实体集合. 假设你有如图5-28所示的概念模型 图5-28 一个包含movies(电影)和它的categories(目录)的模

《Entity Framework 6 Recipes》中文翻译系列 (37) ------ 第六章 继承与建模高级应用之独立关联与外键关联

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 6-13  在基类中应用条件 问题 你想从一个已存在的模型中的实体派生一个新的实体,允许基类被实例化. 解决方案 假设你有如图6-20所示的模型. 图6-20 包含Invoice实体的模型 这个模型只包含一个单独的实体Invoice(发货单).我们想从Invoice派生一个新的实体,它表示删除掉的发货单.这将允许我们以更清晰的业务逻辑来分别对有效的发货单和已删除掉的发货进行不同的操作.按下面

sqlalchemy多外键关联

一.前言 如果有张表A的多个字段关联另一张表B的一个字段,就如同一个客户表的账单地址和发货地址,同时关联地址表中的id字段. 二.事例 # -*- coding: UTF-8 -*- from sqlalchemy import create_engine from sqlalchemy import Integer, ForeignKey, String, Column from sqlalchemy.ext.declarative import declarative_base from s

sqlalchemy操作----外键关联,relationship

... #!_*_coding:utf-8_*_ #__author__:"Alex huang" import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column,Integer,String,ForeignKey from sqlalchemy.orm import r

Django 之 ORM表之间的外键关联与多对多

实现环境表结构: models.py表单创建与代码 from django.db import models # Create your models here. class Publisher(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=64,null=False,unique=True) def __str__(self): return "publishe

Entity Framework - 基于外键关联的单向一对一关系

代码的世界,原以为世界关系很简单,确道是关系无处不在.NET世界里ORM框架中EntityFramework作为其中翘楚,大大解放了搬砖工作的重复工作,着实提高了不少生产力,而也碰到过不少问题!比如关系的映射! 一对一关系的映射: 用户账户密码信息表:包含用户名 密码 邮箱等账户登录时的信息 public class SystemAccount { public SystemAccount() { Id = DateUtils.GeneratedNewGuid(); } public Guid

MySQL里创建外键时错误的解决

--MySQL里创建外键时错误的解决--------------------------------2014/04/30在MySQL里创建外键时(Alter table xxx add constraint fk_xxx foreign key),提示错误,但只提示很简单的信息:ERROR 1005 (HY000): Can't create table '.\env_mon\#sql-698_6.frm' (errno: 150).根本起不到解决问题的作用.要看错误的详细提示,可以使用命令:(

hibernate一对一双向外键关联

一对一双向外键关联:双方都持有对方的外键关联关系. 主控方和一对一单向外键关联的情况是一样的,主要的差异表现为,被空方需要添加: @OneToOne(mappedBy="card") //被控方 主控方必须交给其中的一方去控制,因为不可以双方都同时拥有控制对方的权利,假如是这样的话是没有办法保存成功的.这就是为什么需要指定mappenBy="card"的原因. 1.IdCard.java实体类: package oto_bfk; import javax.persi

存在外键关联的主表truncate如何做

主外键是数据库提供的一种两表之间强制关联的方法,也可以从应用层实现. 优点 缺点 数据库实现的主外键 由数据库层机制保证,无需应用额外实现 强关联,不易扩展变更 应用实现的主外键 易扩展变更 完全由应用控制,要求较高 我认为需要根据实际情况进行取舍,例如表不复杂,可以由应用实现,若表之间关联较多且复杂,那么交由数据库处理,至少保证不会错. 存在主外键关联的主表,由于存在外键关联关系,因此有些操作就会禁止,例如truncate. 实验 1. 创建测试表 SQL> create table tbl_