阿布云

你所需要的,不仅仅是一个好用的代理。

Django的模型和数据库以及一点总结(四)

阿布云 发表于

p4.png

使用原生orm

如果你需要直接访问数据库,则格式如下:

from django.db import connectiondef my_custom_sql(self):

    cursor = connection.cursor()

    cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])

    cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])

    row = cursor.fetchone()

    return row

延迟加载

注意 延迟加载 的问题,在每次访问的时候,会从加载中返回指定内容并且指向下一个。

数据库事务

管理事务

使用默认的管理事务

Django中提供了一种默认的方式,只需要在Settting中将ATOMIC_REQUESTS设置为True,其原理就是将把每一个请求用事务包装起来,在调用一个view里面的方法之前Django先创建一个事务,如果发出的响应没有问题,Django就会提交这个事务。如果在view这里产生一个异常,Django就会回滚这次事务。

但是,这也存在一个效率问题,对每个视图开启一个事务是有所耗费的。其对性能的影响依赖于应用程序对数据库的查询语句效率和数据库当前的锁竞争情况。

如果想要阻止视图运行一个事务,则可以在view上使用non_atomic_requests装饰器,那么对应的view则不会执行一个事务。

from django.db import transaction@transaction.non_atomic_requests(using='other')def my_other_view(request):

    do_stuff_on_the_other_database()

自定义管理事务

通过atomic(using=None, savepoint=True)装饰器对具体的view进行管理事务,它也可以作为上下文管理器。

from django.db import transaction@transaction.atomicdef viewfunc(request):

    # This code executes inside a transaction.

    do_stuff()def viewfunc(request):

    # This code executes in autocommit mode (Django's default).

    do_stuff()

    with transaction.atomic():

        # This code executes inside a transaction.

        do_more_stuff()

在底层,Django的事务管理代码:

  • 当进入到最外层的 atomic 代码块时会打开一个事务;
  • 当进入到内层atomic代码块时会创建一个保存点;
  • 当退出内部块时会释放或回滚保存点;
  • 当退出外部块时提交或回退事物。

同样的存在一个性能的考虑,所有打开的事务会对数据库带来性能成本。要尽量减少这种开销,尽量保持您的交易尽可能短。 在Django的请求/响应周期,如果你使用 atomic()来执行长运行的进程,这尤其重要。

使用原生的api

具体参考

数据库访问优化

在模版中使用with标签

{% with total=business.employees.count %}

    {{ total }} employee{{ total|pluralize }}{% endwith %}

在数据库中操作而不是在Python中操作

  • 在最基础的层面上,使用过滤器和返现过滤器对数据库进行过滤;
  • 使用**F表达式**在相同模型中基于其它字段进行过滤;
  • 使用数据库中的aggregate和annotate;

用唯一的被索引的列来当作检索的对象

比如,上面的就快过于下面,因为id被数据库索引,而且是唯一的:

entry = Entry.objects.get(id=10)entry = Entry.object.get(headline="News Item Title")

一次性检索需要的东西

通过使用 select_related 和 prefetch_related ,前者限于单值关系 - 外键和一对一关系,后者允许它预取多对多和多对一对象。它们都可以实现对数据的缓存。

获取你想要的

使用QuerySet.values()和values_list()

当你仅仅想要一个带有值的字典或者列表,并不需要使用ORM模型对象时,可以适当使用values()。对于在模板代码中替换模型对象,这样会非常有用 —— 只要字典中带有的属性和模板中使用的一致,就没问题。

使用QuerySet.defer()和only()

如果一些数据库的列你并不需要(或者大多数情况下并不需要),使用defer()和only()来避免加载它们。注意如果你确实要用到它们,ORM会在另外的查询之中获取它们。如果你不能够合理地使用这些函数,不如不用。

另外,当建立起一个带有延迟字段的模型时,要意识到一些(小的、额外的)消耗会在Django内部产生。不要不分析数据库就盲目使用延迟字段,因为数据库必须从磁盘中读取大多数非text和VARCHAR数据,在结果中作为单独的一行,即使其中的列很少。 defer()和only()方法在你可以避免加载大量文本数据,或者可能要花大量时间处理而返回给Python的字段时,特别有帮助。像往常一样,应该先写出个大概,之后再优化。

使用QuerySet.count()

如果你想要获取大小,不要使用 len(queryset)

使用QuerySet.exists()

如果你想要知道是否存在至少一个结果,不要使用if queryset

不要过度使用count和exists

如果前面已经将某个数据进行了缓存,如果再使用count或者exist则会造成额外的查询。比如下面的情况就是比较好的使用方法。

{% if display_inbox %}

  {% with emails=user.emails.all %}

    {% if emails %}

      <p>You have {{ emails|length }} email(s)</p>

      {% for email in emails %}

        <p>{{ email.body }}</p>

      {% endfor %}

    {% else %}

      <p>No messages today.</p>

    {% endif %}

  {% endwith %}{% endif %}

使用QuerySet.update()和delete()

通过QuerySet.update()使用批量的SQL UPDATE语句,而不是获取大量对象,设置一些值再单独保存。与此相似,在可能的地方使用批量deletes。

但是要注意,这些批量的更新方法不会在单独的实例上面调用save()或者delete()方法,意思是任何你向这些方法添加的自定义行为都不会被执行,包括由普通数据库对象的信号驱动的任何方法。

直接使用外键的值

如果你仅仅需要外键当中的一个值,要使用对象上你已经取得的外键的值,而不是获取整个关联对象再得到它的主键。例如,执行:

entry.blog_id # 正确entry.blog.id # 不好

没有需要就不需要进行排序

排序并不是没有代价的;每个需要排序的字段都是数据库必须执行的操作。如果一个模型具有默认的顺序(Meta.ordering),并且你并不需要它,通过在查询集上无参调用order_by() 来移除它。

向你的数据库添加索引可能有助于提升排序性能。

整体插入

创建对象时,尽可能使用bulk_create()来减少SQL查询的数量。或者是在ManyToManyFields上。

Entry.objects.bulk_create([

    Entry(headline="Python 3.0 Released"),

    Entry(headline="Python 3.1 Planned")

])my_band.members.add(me, my_friend)