django】【Python基础】django默认数据库是什么

2020-12-25 11:43发布

5条回答
summer
2楼 · 2020-12-25 16:36

Django的默认数据库是sqlite3


张成秀 - 快乐开心每一天
3楼 · 2020-12-26 10:39

默认的数据库sqlite3,除了sqlite3,还支持postgresql、mysql、oracle

配置如下:其中postgresql_psycopg2为postgresql的适配器。


敦敦宁
4楼 · 2020-12-26 10:47

1. 过滤器聚合(Aggregation with Filter)

在 Django 2.0 之前,如果我们想要得到诸如用户总数和活跃用户总数之类的东西,我们不得不求助于条件表达式:

from django.contrib.auth.models import Userfrom django.db.models import (
    Count,
    Sum,
    Case,
    When,
    Value,
    IntegerField,)User.objects.aggregate(
    total_users=Count('id'),
    total_active_users=Sum(Case(
        When(is_active=True, then=Value(1)),
        default=Value(0),
        output_field=IntegerField(),
    )),)

在 Django 2.0 中,添加了聚合函数的过滤器参数,使其更容易:

from django.contrib.auth.models import Userfrom django.db.models import Count, FUser.objects.aggregate(
    total_users=Count('id'),
    total_active_users=Count('id', filter=F('is_active')),)

很棒,又短又可口

如果你正在使用 PostgreSQL,这两个查询将如下所示:

SELECT
    COUNT(id) AS total_users,
    SUM(CASE WHEN is_active THEN 1 ELSE 0 END) AS total_active_usersFROM
    auth_users;SELECT
    COUNT(id) AS total_users,
    COUNT(id) FILTER (WHERE is_active) AS total_active_usersFROM
    auth_users;

第二个查询使用了 WHERE 过滤子句。


2. 查询集的结果变为具名元组(QuerySet results as namedtuples)

我是一个 namedtuples 的粉丝,同时也是 Django 2.0 的 ORM 的粉丝。

在 Django 2.0 中,values_list 方法的参数中添加了一个叫做 named 的属性。将 named 设置为 True 会将 QuerySet 作为 namedtuples 列表返回:

> user.objects.values_list(
    'first_name',
    'last_name',)[0](‘Haki’, ‘Benita’)> user_names = User.objects.values_list(
    'first_name',
    'last_name',
    named=True,)> user_names[0]Row(first_name='Haki', last_name='Benita')> user_names[0].first_name'Haki'> user_names[0].last_name'Benita'

3. 自定义函数(Custom functions)

Django 2.0 的 ORM 功能非常强大,而且特性丰富,但还是不能与所有数据库的特性同步。不过幸运的是,ORM让我们用自定义函数来扩展它。

假设我们有一个记录报告的持续时间字段,我们希望找到所有报告的平均持续时间:

from django.db.models import AvgReport.objects.aggregate(avg_duration=Avg(‘duration’))> {'avg_duration': datetime.timedelta(0, 0, 55432)}

那很棒,但是如果只有均值,信息量有点少。我们再算出标准偏差吧:

from django.db.models import Avg, StdDevReport.objects.aggregate(
    avg_duration=Avg('duration'),
    std_duration=StdDev('duration'),)ProgrammingError: function stddev_pop(interval) does not existLINE 1: SELECT STDDEV_POP("report"."duration") AS "std_dura...
               ^HINT:  No function matches the given name and argument types.You might need to add explicit type casts.

呃... PostgreSQL 不支持间隔类型字段的求标准偏差操作,我们需要将时间间隔转换为数字,然后才能对它应用 STDDEV_POP 操作。

一个选择是从时间间隔中提取:

SELECT
    AVG(duration),
    STDDEV_POP(EXTRACT(EPOCH FROM duration))FROM 
    report;

      avg       |    stddev_pop    ----------------+------------------ 00:00:00.55432 | 1.06310113695549(1 row)

那么我们如何在 Django 中实现呢?你猜到了 -- 一个自定义函数:

# common/db.pyfrom django.db.models import Funcclass Epoch(Func):
   function = 'EXTRACT'
   template = "%(function)s('epoch' from %(expressions)s)"

我们的新函数这样使用:

from django.db.models import Avg, StdDev, Ffrom common.db import EpochReport.objects.aggregate(
    avg_duration=Avg('duration'), 
    std_duration=StdDev(Epoch(F('duration'))),){'avg_duration': datetime.timedelta(0, 0, 55432),
 'std_duration': 1.06310113695549}

*注意在 Epoch 调用中使用 F 表达式。


4. 声明超时(Statement Timeout)

这可能是我给的最简单的也是最重要的提示。我们是人类,我们都会犯错。我们不可能考虑到每一个边缘情况,所以我们必须设定边界。

与其他非阻塞应用程序服务器(如 Tornado,asyncio 甚至 Node)不同,Django 通常使用同步工作进程。这意味着,当用户执行长时间运行的操作时,工作进程会被阻塞,完成之前,其他人无法使用它。

应该没有人真正在生产中只用一个工作进程来运行 Django,但是我们仍然希望确保一个查询不会浪费太多资源太久。

在大多数 Django 应用程序中,大部分时间都花在等待数据库查询上了。所以,在 SQL 查询上设置超时是一个很好的开始。

我喜欢像这样在我的 wsgi.py 文件中设置一个全局超时:

# wsgi.pyfrom django.db.backends.signals import connection_createdfrom django.dispatch import receiver@receiver(connection_created)def setup_postgres(connection, **kwargs):
    if connection.vendor != 'postgresql':
        return
    
    # Timeout statements after 30 seconds.
    with connection.cursor() as cursor:
        cursor.execute("""            SET statement_timeout TO 30000;        """)

为什么是 wsgi.py? 因为这样它只会影响工作进程,不会影响进程外的分析查询,cron 任务等。

希望您使用的是持久的数据库连接,这样每次请求都不会再有连接开销。

超时也可以配置到用户粒度:

postgresql=#> alter user app_user set statement_timeout TO 30000;
ALTER ROLE

题外话:我们花了很多时间在其他常见的地方,比如网络。因此,请确保在调用远程服务时始终设置超时时间:

import requestsresponse = requests.get(
    'https://api.slow-as-hell.com',
    timeout=3000,)

5. 限制(Limit)

这与设置边界的最后一点有些相关。有时我们的客户的一些行为是不可预知的

比如,同一用户打开另一个选项卡并在第一次尝试「卡住」时再试一次并不罕见。


这就是为什么要限制


我们限制某一个查询的返回不超过 100 行数据:

# bad exampledata = list(Sale.objects.all())[:100]

这很糟糕,因为虽然只返回 100 行数据,但是其实你已经把所有的行都取出来放进了内存。

我们再试试:

data = Sale.objects.all()[:100]

这个好多了,Django 会在 SQL 中使用 limit 子句来获取 100 行数据。

我们增加了限制,但我们仍然有一个问题 -- 用户想要所有的数据,但我们只给了他们 100 个,用户现在认为只有 100 个数据了。

并非盲目的返回前 100 行,我们先确认一下,如果超过 100 行(通常是过滤以后),我们会抛出一个异常:

LIMIT = 100if Sales.objects.count() > LIMIT:
    raise ExceededLimit(LIMIT)return Sale.objects.all()[:LIMIT]

挺有用,但是我们增加了一个新的查询

能不能做的更好呢?我们可以这样:

LIMIT = 100data = Sale.objects.all()[:(LIMIT + 1)]if len(data) > LIMIT:
    raise ExceededLimit(LIMIT)return data

我们不取 100 行,我们取 100 + 1 = 101 行,如果 101 行存在,那么我们知道超过了 100 行:

记住 LIMIT + 1 窍门,有时候它会非常方便


6. 事务与锁的控制

这个比较难。

由于数据库中的锁机制,我们开始在半夜发现事务超时错误。

(看来这个作者之前经常被半夜叫醒 ?)

在我们的代码中操作事务的常见模式如下所示:

from django.db import transaction as db_transaction...with db_transaction.atomic():
  transaction = (
        Transaction.objects
        .select_related(
            'user',
            'product',
            'product__category',
        )
        .select_for_update()
        .get(uid=uid)
  )
  ...

事务操作通常会涉及用户和产品的一些属性,所以我们经常使用 select_related 来强制 join 并保存一些查询。

更新交易还会涉及获得一个锁来确保它不被别人获得。

现在,你看到问题了吗?没有?我也没有。(作者好萌)

我们有一些晚上运行的 ETL 进程,主要是在产品和用户表上做维护。这些 ETL 操作会更新字段然后插入表,这样它们也会获得了表的锁。

那么问题是什么?当 select_for_update 与 select_related 一起使用时,Django 将尝试获取查询中所有表的锁。

我们用来获取事务的代码尝试获取事务表、用户、产品、类别表的锁。一旦 ETL 在午夜锁定了后三个表,交易就开始失败。

一旦我们对问题有了更好的理解,我们就开始寻找只锁定必要表(事务表)的方法。(又)幸运的是,select_for_update 的一个新选项在 Django 2.0 中可用:

from django.db import transaction as db_transaction...with db_transaction.atomic():
  transaction = (
        Transaction.objects
        .select_related(
            'user',
            'product',
            'product__category',
        )
        .select_for_update(
            of=('self',)
        )
        .get(uid=uid)
  )
  ...

这个 of 选项被添加到 select_for_update ,使用 of 可以指明我们要锁定的表,self 是一个特殊的关键字,表示我们要锁定我们正在处理的模型,即事务表。

目前,该功能仅适用于 PostgreSQL 和 Oracle。


7. 外键索引(FK Indexes)

创建模型时,Django 会在所有外键上创建一个 B-Tree 索引,它的开销可能相当大,而且有时候并不很必要。

典型的例子是 M2M(多对多)关系的直通模型:

class Membership(Model):
    group = ForeignKey(Group)
    user = ForeignKey(User)

在上面的模型中,Django 将会隐式的创建两个索引:一个用于用户,一个用于组。

M2M 模型中的另一个常见模式是在两个字段一起作为一个唯一约束。在这种情况下,意味着一个用户只能是同一个组的成员,还是那个模型:

class Membership(Model):
    group = ForeignKey(Group)
    user = ForeignKey(User)
    class Meta:
        unique_together = (
           'group',
           'user',
        )

这个 unique_together 也会创建两个索引,所以我们得到了个字段个索引的模型 ?

根据我们用这个模型的职能,我们可以忽略 FK 索引,只保留唯一约束索引:

class Membership(Model):
    group = ForeignKey(Group, db_index=False)
    user = ForeignKey(User, db_index=False)
    class Meta:
        unique_together = (
            'group',           
            'user',
        )

删除冗余的索引将会是插入和查询更快,而且我们的数据库更轻量。


8. 组合索引中列的顺序(Order of columns in composite index)

具有多个列的索引称为组合索引。 在 B-Tree 组合索引中,第一列使用树结构进行索引。从第一层的树叶为第二层创建一棵新树,以此类推。

索引中列的顺序非常重要。

在上面的例子中,我们首先会得到一个组(group)的树,另一个树是所有它的用户(user)。

B-Tree 组合索引的经验法则是使二级索引尽可能小。换句话说,高基数(更明确的值)的列应该是在第一位的。

在我们的例子中,假设组少于用户(一般),所以把用户列放在第一位会使组的二级索引变小。

class Membership(Model):
    group = ForeignKey(Group, db_index=False)
    user = ForeignKey(User, db_index=False)
    class Meta:
        unique_together = (
            'user',
            'group',
        )

*注意元组里面的字段名顺序

这只是一个经验法则,最终的索引应该针对特定的场景进行优化。这里的要点是要知道隐式索引和组合索引中列顺序的重要性。(临书:惹不起惹不起)


9. 块范围索引(BRIN indexes)

B-Tree 索引的结构像一棵树。查找单个值的成本是随机访问表的树的高度 + 1。这使得 B-Tree 索引非常适合独特的约束和(一些)范围查询。

B-Tree索引的缺点是它的大小 -- B-Tree 索引可能会变大。

没有其他选择了吗?并不是,数据库为特定用例提供其他类型的索引也蛮多的。

从 Django 1.11 开始,有一个新的 Meta 选项用于在模型上创建索引。这给了我们探索其他类型索引的机会。

PostgreSQL 有一个非常有用的索引类型 BRIN(块范围索引)。在某些情况下,BRIN 索引可以比 B-Tree 索引更高效。

我们看看官网文档怎么说的:

BRIN 设计用于处理非常大的表格,其中某些列与表格内的物理位置有一些自然的相关性。

要理解这个陈述,了解 BRIN 索引如何工作是很重要的。顾名思义,BRIN 索引会在表格中的一系列相邻块上创建一个小型索引。该索引非常小,只能说明某个值是否在范围内,或者是否在索引块范围内。

我们来做一个 BRIN 索引如何帮助我们的简单例子。

假设我们在一列中有这些值,每一个都是一个块:

1, 2, 3, 4, 5, 6, 7, 8, 9

我们为每三个相邻的块创建一个范围:

[1,2,3], [4,5,6], [7,8,9]

对于每个范围,我们将保存范围内的最小值和最大值:

[1–3], [4–6], [7–9]

我们尝试通过此索引搜索 5:

  • [1–3] —  绝对没在这里

  • [4–6] — 可能在这里

  • [7–9] — 绝对没在这里

使用索引,我们限制了我们搜索的范围在 [4-6] 范围。

再举一个例子,这次列中的值不会被很好地排序:

[2–9], [1–7], [3–8]

再试着查找 5:

  • [2–9] — 可能在这里

  • [1–7] — 可能在这里

  • [3–8] — 可能在这里

索引是无用的 -- 它不仅没有限制搜索,实际上我们不得不搜索更多,因为我们同时提取了索引和整个表。

回到文档:

...列与表格内的物理位置有一些自然的相关性

这是 BRIN 索引的关键。为了充分利用它,列中的值必须大致排序或聚集在磁盘上。

现在回到 Django,我们有哪些常被索引的字段,最有可能在磁盘上自然排序?没错,就是 auto_now_add。(这个很常用,没用到的小伙伴可以了解下)

Django 模型中一个非常常见的模式是:

class SomeModel(Model):    
    created = DatetimeField(
        auto_now_add=True,
    )

当使用 auto_now_add 时,Django 将自动使用当前时间填充该行的时间。创建的字段通常也是查询的绝佳候选字段,所以它通常被插入索引。

让我们在创建时添加一个 BRIN 索引:

from django.contrib.postgres.indexes import BrinIndexclass SomeModel(Model):
    created = DatetimeField(
        auto_now_add=True,
    )
    class Meta:
        indexes = (
            BrinIndex(fields=['created']),
        )

为了了解大小的差异,我创建了一个约 2M 行的表,并在磁盘上自然排序了日期字段:

  • B-Tree 索引:37 MB

  • BRIN 索引:49 KB

没错,你没看错。

创建索引时要考虑的要比索引的大小要多得多。但是现在,通过 Django 1.11 支持索引,我们可以轻松地将新类型的索引整合到我们的应用程序中,使它们更轻,更快。


flame
5楼 · 2020-12-28 11:11

默认的数据库sqlite3,除了sqlite3,还支持postgresql、mysql、oracle

配置如下:其中postgresql_psycopg2为postgresql的适配器。


锦衣夜行1
6楼 · 2021-01-09 16:08

没有配置的话默认数据库是sqlite,可以在setting里的DATABASES进行数据库配置

相关问题推荐

  • 回答 19

    Django是一个开放源代码的Web应用框架,由Python写成。采用了MVT的框架模式,即模型M,视图V和模板T。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,即是CMS(内容管理系统)软件。并于2005年7月在BSD许可证下发布。这套框架是以比...

  • 回答 1

    使用类似pymysql的第三方模块,在Python代码中嵌入SQL语句,可以直接访问数据库。对于轻量级的SQLite,Django和Python原生支持,连第三方模块都不需要就可以访问。

  • 回答 3
    已采纳

    FlaskFlask确实很轻,不愧是Micro Framework,从Django转向Flask的开发者一定会如此感慨,除非二者均为深入使用过Flask自由、灵活,可扩展性强,第三方库的选择面广,开发时可以结合自己最喜欢用的轮子,也能结合最流行最强大的Python库入门简单,即便没有多...

  • 回答 4
    已采纳

    打开 Linux 或 MacOS 的 Terminal (终端)直接在 终端中输入windows 快捷键 win + R,输入 cmd,直接在 cmd 上输入1、新建一个项目django-admin.py startproject 项目名1以下命令要先进入项目目录下才能执行:cd 项目名2、新建app (一个项目可以有多个app...

  • 回答 3

    Django中避免sql注入的方法:1、对用户的输入进行校验;2、不要使用动态拼装sql;3、不要把机密信息直接存放;4、应用的异常信息应该给出尽可能少的提示;5、利用Dajngo的ORM来有效避免sql注入。什么是SQL注入?所谓SQL注入,就是通过把SQL命令插入到Web表单...

  • 回答 1

    中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能...

  • 回答 1

    项目主要页面介绍1.首页2.注册3.登录2.项目开发模式开发模式前后端不分离后端框架Django+Jinja2模板引擎前端框架Vue.js3.准备项目代码仓库源码托管网站1、码云(https://gitee.com/)2、创建源码远程仓库:website3、克隆项目代码仓库新建文件夹下载githttps:...

  • 回答 3

    from django.http import FileResponsefrom django.utils.encoding import escape_uri_path def build_download_response(filepath, filename):        构建下载文件的文件头    :param filepath: 文件路径    :param filenam...

  • 回答 2

    Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出。中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性。我们可以使用中间件,在Django处理视图的不同阶段对输入或输出...

  • 回答 10

    django现在在生产环境用得还是比较多的,但是只能说数量比较多,质量很差;意思就是,越大型的项目越不会选django,因为它封装得太好,不够灵活,一般快速上马项目可以,但是长期维护下来很难受一般如果有开发团队的都选flask和tornado...

  • 回答 2

    什么是 middleware什么时候使用 middleware我们写 middleware 必须要记住的东西写一些 middlewares 来理解中间件的工作过程和要点什么是 middlewareMiddlewares 是修改 Django request 或者 response 对象的钩子. 下面是Django 文档中的一段描述。Middlew...

  • 回答 2

    客户端与服务端交互(知识点)浏览器就是客户端服务端开放端口和连接即可客户端服务器代码实现# -*- coding: utf-8 -*-__author__ = 'HeYang'__time__ = '2018/6/7 2:57'import socket sock = socket.socket()sock.bind(('1...

  • 回答 11

    Django中间件有很多,入session、csrf、contexttype等,能力强的朋友还可以自己写中间件。

  • 回答 4

    django中间件主要有两个作用1·中间件是介于request 和 response 处理之间的一道处理过程,用于全局范围改变Django的输入和输出,简单的来说中间件是帮助我们在视图函数指向之前和执行之后都可以做一些额外的操作2·Django项目中默认启用了csrf保护,每次请求...

  • 回答 7

    中间件 Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出。中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性。中间件Django中的中间件是一个轻量级、底层的插件系统...

没有解决我的问题,去提问