Discuss / Python / 分享一些遇到的坑

分享一些遇到的坑

Topic source

慈眉杏眼

#1 Created at ... [Delete] [Delete and Lock User]

分享一些遇到的坑

数据库无法连接

用前面的 mysql.connector 是可以正常连接数据库的,但是到了这里用 aiomysql 的时候就无法连接,错误信息:

/usr/bin/python3 /home/frank/python/awesome-python-webapp/www/tests/mORMtest.py
Traceback (most recent call last):
  File "/home/frank/.local/lib/python3.8/site-packages/aiomysql/connection.py", line 503, in _connect
    await self._request_authentication()
  File "/home/frank/.local/lib/python3.8/site-packages/aiomysql/connection.py", line 785, in _request_authentication
    auth_packet = await self._read_packet()
  File "/home/frank/.local/lib/python3.8/site-packages/aiomysql/connection.py", line 593, in _read_packet
    packet.check_error()
  File "/home/frank/.local/lib/python3.8/site-packages/pymysql/protocol.py", line 220, in check_error
    err.raise_mysql_exception(self._data)
  File "/home/frank/.local/lib/python3.8/site-packages/pymysql/err.py", line 109, in raise_mysql_exception
    raise errorclass(errno, errval)
pymysql.err.InternalError: (1049, "Unknown database 'mORMtesst'")

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/frank/python/awesome-python-webapp/www/tests/mORMtest.py", line 34, in <module>
    m_loop.run_until_complete(doSomething(m_loop))
  File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File "/home/frank/python/awesome-python-webapp/www/tests/mORMtest.py", line 18, in doSomething
    await create_pool(
  File "/home/frank/python/awesome-python-webapp/www/mORM/utils.py", line 9, in create_pool
    __pool = await aiomysql.create_pool(
  File "/home/frank/.local/lib/python3.8/site-packages/aiomysql/pool.py", line 29, in _create_pool
    await pool._fill_free_pool(False)
  File "/home/frank/.local/lib/python3.8/site-packages/aiomysql/pool.py", line 167, in _fill_free_pool
    conn = await connect(echo=self._echo, loop=self._loop,
  File "/home/frank/.local/lib/python3.8/site-packages/aiomysql/connection.py", line 75, in _connect
    await conn._connect()
  File "/home/frank/.local/lib/python3.8/site-packages/aiomysql/connection.py", line 521, in _connect
    raise OperationalError(2003,
pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on '0.0.0.0'")

进程已结束,退出代码1

由于报错比较长,我一开始只注意到了最后一句 

Can't connect to MySQL server on '0.0.0.0'

以为是 IP 地址的问题,但是改成 localhost127.0.0.1 都不行,找网上的方法 

GRANT ALL ON *.* to  <user>@<my_ip> IDENTIFIED BY <my_password>;

这个能生效但是依然有错误,然后仔细看错误信息,终于在中间发现了

pymysql.err.InternalError: (1049, "Unknown database 'mORMtesst'")

原来是数据库不存在导致的,以前用 mysql.connector 的时候连接的都是 MySQL 安装时附带的 test 数据库,这次使用了一个新的数据库所以就出错了,手动创建一个数据库就可以了。

其他问题

首先如果你看得一知半解的话,建议直接下载文后链接的源码下来先试试,不然测试就让人很头大

其次你也可以参考 orm 包的实现方式,它在 python 路径的 site-packages 下面,Ubuntu 下面的路径是 ~/.local/lib/python3.8/site-packages/orm,直接用 VSCode 之类的打开这个文件夹,里面内容很少,值得一看

分享一个动态创建表的代码

SQL 中有 CREATE TABLE IF NOT EXISTS 的用法,可以在表不存在的时候创建,我们这里就利用它,添加一个名为 create() 的 @classmethod,具体如下:

首先在 ModelMetaClass 中 __new__(mcs, name, bases, attrs) 的定义中靠后在 attrs['__delete__'] 的地方添加以下代码:

# attrs['__delete__'] 是原先的
attrs['__delete__'] = 'delete from `%s` where `%s`=?' % (tableName, primaryKey)
# 以下是新增的代码
attrs['__create__'] = "create table if not exists `%s` (%s, primary key (`%s`)) engine=InnoDB default charset=utf8mb4;" % (tableName, get_column_string(mappings), mappings.get(primaryKey).name or primaryKey)
# 以上是新增的代码
return type.__new__(mcs, name, bases, attrs)

即为类添加一个 __create__,这里放的是建表语句,首先 create table if not exists 在表不存在的时候才建表,后面的第一个 %s 是表名,前面已经将其存放在了 tableName 中;第二个 %s 是每个列连成的字符串,每一列的格式为 `列名` 类型 [not null],这里使用 get_column_string() 函数生成,具体见后文;第三个 %s 是主键的列名,如果新建 Model 类的时候在 Field 中指定了主键的 name 则直接使用,否则使用变量名。

接下来是 get_column_string() 函数:

def get_column_string(mappings) -> str:
    return ', '.join(map(lambda s: "`%s` %s %s" % (
                        s[1].name or s[0],       
                        s[1].column_type, 
                        '' if s[1].nullable else 'not null'),     
                        mappings.items()))

这个函数接收的参数是 mappings 这个字典,字典的键是变量名,值是 Field。从前往后看的话,首先在每一列之间添加逗号,然后使用一个 map 函数,将 mappings.items() 中的每一个 item 都赋值给 s,这每个 item 都是一个 lists[0] 是键,也就是变量名,s[1] 是值,也就是 Field,然后将它们格式化成字符串,形式就是上面说的 `列名` 类型 [not null] ,如果 Field 中指定了 name 则使用,没有则使用变量名。这里说一下,我的 Field 中增加了一个 nullable 属性,用于确定它是否可以为空,和教程的略有不同。

然后在 Model 类定义中增加一个类函数(该叫函数还是叫方法?不知道):

@classmethod
async def create(cls):
    """ Create table if table (with the same name) not exists. """
    await execute(cls.__create__)

就大功告成了,在 __main__ 中执行的时候,create_pool 成功之后,直接调用

# 调用这一句
await User.create()

# (这是 User 的定义)
class User(Model):
    id = StringField(primary_key=True)
    name = StringField()

就可以创建表了,如果表已经存在,控制台会打印出一个 Warning,忽略即可。

慈眉杏眼

#2 Created at ... [Delete] [Delete and Lock User]

折叠部分还有动态创建表的代码,点 Read More 查看。

感谢分享~~

动态创建代码,还有待消化


  • 1

Reply