谈谈Django Models的create都做了些什么。

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_37422289/article/details/81640995

主要问题

Model.objects.create()都完成了哪些操作.

基本功能

根据官方文档django document 的介绍只有一句话:

A convenience method for creating an object and saving it all in one step.

create()是一个将
1) 创建类对象;
2) 保存进数据库.
两个操作合二为一的语句.

示例:

p = Person.objects.create(first_name="Bruce", last_name="Springsteen")

和:

p = Person(first_name="Bruce", last_name="Springsteen")
p.save(force_insert=True)

属于等价的操作.
这基本上就是create()的常用场景,然而今天的实践中却遇到了奇怪的现象.

问题描述:

model当中存在一个DateTimeField()

class TestModel(models.Model):
    test_datefield = models.DateTimeField()

往数据中写入一个实例, 再取出:

In [2] TestModel.objects.create(test_datefield='2018-08-14')
Out[2]: <TestModel: TestModel object (1)>
In [3]: test = TestModel.objects.get(pk=1)
In [4]: test.test_datefield
Out[4]: datetime.datetime(2018, 8, 14, 0, 0, tzinfo=<UTC>)

一切正常, django 的DateTimeField()帮我们完成了从字符串的日期转换为datetime类型,再写入数据库对应格式的过程, 这显然不用我们操心.
存进去时是str类型的数据, 取出来已经是datetime类型.

但当我想用create()的返回值, 对数据库中的日期数据进行操作时:

In [5]: obj = TestModel.objects.create(test_datefield='2018-08-15')
In [6]: obj.test_datefield
Out[6]: '2018-08-15'
In [7]: type(obj.test_datefield)
Out[7]: str

会发现obj的test_datefield字段依然是一个str变量, 与get()方法从数据库取出来的对象对比:

In [8]: type(test)
Out[8]: snippets.models.TestModel

In [9]: type(obj)
Out[9]: snippets.models.TestModel

尽管显示type相同,但是同个字段test_datefield的类型却截然不同,当然也没法在后者当中直接对日期值进行操作, 需使用strptime()函数对str类型进行操作之后才可进行日期相关的操作.

源码分析

class QuerySet:
    """Represent a lazy database lookup for a set of objects."""

    def create(self, **kwargs):
        """
        Create a new object with the given kwargs, saving it to the database
        and returning the created object.
        """
        obj = self.model(**kwargs) #调用
        self._for_write = True
        obj.save(force_insert=True, using=self.db)
        return obj

在源码当中已经可以看到原因, 确实如同官方文档所述, 一个create()相当于 一次Model()创建对象, 并调用save()完成保存.(self._for_write = True 表明是一次对数据库的加写锁操作, 防止线程不安全)

The save() method has no return value.

save()本身没有返回值, 仅完成写入数据库的操作(实际上是lazy lookup延时操作, 在此不展开)
因此return的依然是obj,也就是说create()的返回值和调用model()完成初始化的返回值是相同的, 返回值obj并不是从数据库中获取的.这就是obj.test_datefield的值依然是python的基础类型str的原因.

对比get

源码:

def get(self, *args, **kwargs):
    """
    Perform the query and return a single object matching the given
    keyword arguments.
    """
    clone = self.filter(*args, **kwargs)
    if self.query.can_filter() and not self.query.distinct_fields:
        clone = clone.order_by()
    num = len(clone)
    if num == 1:
        return clone._result_cache[0]
    if not num:
        raise self.model.DoesNotExist(
            "%s matching query does not exist." %
            self.model._meta.object_name
        )
    raise self.model.MultipleObjectsReturned(
        "get() returned more than one %s -- it returned %s!" %
        (self.model._meta.object_name, num)
    )

可知get()方法是通过filter()拿到queryset之后再判断结果是否唯一, 因此是从数据库中得到的数据. 这也就是为什么上面get()方法的对象test, test_datefield已经是datetime类型, 因为django的整个处理过程是:

model()创建对象–> 结合使用的数据库类型,对于不同类型field有相关的转换方法–>拼接SQL语句,写入数据库.

从get()中获取的对象已经完成了三个步骤, 而create()的返回值依然是第一个步骤的结果.

结论

在某些场景当中, create()的返回值并不能代替get(). 严格来说, create()的作用应该仅限于为创建对象后调用save()提供一个快捷方式.

猜你喜欢

转载自blog.csdn.net/m0_37422289/article/details/81640995