Python | peewee插入数据后获取主键值

发布一下 0 0

最近使用python的ORM框架peewee开发项目,遇到一个问题就是:在插入数据后,获取不到数据库生成的自增主键值,然后分析源码后得到解决方案,以此记录。

首先,定义model:

class User(Model):    id = IntegerField(primary_key=True)        username = CharField()    class Meta:        database = db        db_table = "user"

其中主键id是整型自增类型。

根据peewee官方文档介绍,插入数据时使用如下:

user = User(username='admin')user.save()或user = User.create(username='admin')或id = User.insert(username='admin).execute()

但是获取user.id或id结果却为None。

于是乎开始看peewee的源码,看到底发生了什么?

首先看Moel的create方法:

@classmethoddef create(cls, **query):    inst = cls(**query)    inst.save(force_insert=True)    # 还是调用本类的save方法    inst._prepare_instance()    return inst

发现create方法还是调用本类中得save方法,那么好办了,来看save方法做了些什么?

def save(self, force_insert=False, only=None):    field_dict = dict(self._data)    # 所有属性数据    pk_field = self._meta.primary_key    # 主键列    pk_value = self._get_pk_value()    # 主键值    if only:        field_dict = self._prune_fields(field_dict, only)    self._populate_unsaved_relations(field_dict)    # 根据主键值和强制插入标记,判断是更新还是插入    if pk_value is not None and not force_insert:        if self._meta.composite_key:            for pk_part_name in pk_field.field_names:                field_dict.pop(pk_part_name, None)        else:            field_dict.pop(pk_field.name, None)        rows = self.update(**field_dict).where(self._pk_expr()).execute()    else:        # 插入操作,最终调用本类的insert方法,得到数据库返回的主键值        pk_from_cursor = self.insert(**field_dict).execute()        if pk_from_cursor is not None:            pk_value = pk_from_cursor        # 设置model的主键值        self._set_pk_value(pk_value)        rows = 1    self._dirty.clear()    return rows

通过上面的注释和逻辑,我们终于知道,插入数据最终还是调用本类的insert方法,并执行execute方法,insert方法是这样的:

@classmethoddef insert(cls, **insert):    return InsertQuery(cls, insert)

此方法传入model数据并返回InsertQuery对象,找到此类的execute方法:

def execute(self):    insert_with_loop = (        self._is_multi_row_insert and        self._query is None and        self._returning is None and        not self.database.insert_many)    if insert_with_loop:        return self._insert_with_loop()    if self._returning is not None and self._qr is None:        return self._execute_with_result_wrapper()    elif self._qr is not None:        return self._qr    else:        # 执行插入数据库操作        cursor = self._execute()        if not self._is_multi_row_insert:            if self.database.insert_returning:                pk_row = cursor.fetchone()                meta = self.model_class._meta                clean_data = [                    field.python_value(column)                    for field, column                    in zip(meta.get_primary_key_fields(), pk_row)]                if self.model_class._meta.composite_key:                    return clean_data                return clean_data[0]            # 重点 --> 获取插入数据返回的主键值            return self.database.last_insert_id(cursor, self.model_class)        elif self._return_id_list:            return map(operator.itemgetter(0), cursor.fetchall())        else:            return True 

从上面可以得知,返回主键值是调用了self.database.last_insert_id(cursor, self.model_class),self.database指的是Database实例对象,观察last_insert_id方法:

def last_insert_id(self, cursor, model):     if model._meta.auto_increment:        return cursor.lastrowid

此方法判断model._meta也就是ModelOptions的auto_increment属性是否为True,如果是则返回主键值,否则返回None,那么这个auto_increment是在什么时候设置的呢?

我们找到在创建每个Model时,都会把ModelOptions赋值给_meta属性,而Model的创建,必须经过BaseModel元类创建生成,找到BaseModel的__new__方法,果然,我们看到赋值过程,以及控制auto_increment变量的逻辑:

def __new__(cls, name, bases, attrs):    ...    cls = super(BaseModel, cls).__new__(cls, name, bases, attrs)    # 创建MoelOptions对象    cls._meta = ModelOptions(cls, **meta_options)    ....    for name, attr in cls.__dict__.items():        if isinstance(attr, Field):            if attr.primary_key and model_pk:                raise ValueError('primary key is overdetermined.')            elif attr.primary_key:                # 主键列和名称                model_pk, pk_name = attr, name            else:                fields.append((attr, name))    ...    if model_pk is not False:        model_pk.add_to_class(cls, pk_name)        cls._meta.primary_key = model_pk        # 重点是这里,根据model_pk的类型或者model_pk的属性来标记auto_increment        cls._meta.auto_increment = (            isinstance(model_pk, PrimaryKeyField) or            bool(model_pk.sequence))        cls._meta.composite_key = composite_key    ....    return cls

版权声明:内容来源于互联网和用户投稿 如有侵权请联系删除

本文地址:http://0561fc.cn/182056.html