Django打造大型企业官网-知识点汇总

 本博文只是用于记录Django相关的一些容易遗漏的知识点,并非详细介绍Django文档内容

 一、Django基础部分

1、自定义URL转换器

之前已经学到过一些django内置的url转换器,包括有int、uuid等。有时候这些内置的url转换器并不能满足我们的需求,因此django给我们提供了一个接口可以让我们自己定义自己的url转换器。

自定义url转换器按照以下五个步骤来走就可以了: 
1. 定义一个类,直接继承自object就可以了。 
2. 在类中定义一个属性regex,这个属性是用来限制url转换器规则的正则表达式。 
3. 实现to_python(self,value)方法,这个方法是将url中的值转换一下,然后传给视图函数的。 
4. 实现to_url(self,value)方法,这个方法是在做url反转的时候,将传进来的参数转换后拼接成一个正确的url。
5. 将定义好的转换器,使用`django.urls.converters.register_converter`方法注册到django中。

demo:

from django.urls import register_converter

class CategoryConverter(object):
    # 正则匹配
    regex = r'\w+|(\w+\+\w+)+'

    # 转换后在视图中使用
    def to_python(self,value):
        # python+django+flask
        # ['python','django','flask']
        result = value.split("+")
        return result

    # 使用reverse反转url时调用
    def to_url(self,value):
        # value:['python','django','flask']
        # python+django+flask
        if isinstance(value,list):
            result = "+".join(value)
            return result
        else:
            raise RuntimeError("转换url的时候,分类参数必须为列表!")

register_converter(CategoryConverter,'cate')  # 注册到Django

 


二、Django模板语言 

 1、for...in...标签

 1.1、遍历字典的时候,需要使用`items`、`keys`和`values`等方法。在`DTL`中,执行一个方法不能使用圆括号的形式。遍历字典示例代码如下:

# 模板代码

{% for key,value in person.items %}
<p>key:{{ key }}</p>
<p>value:{{ value }}</p>
{% endfor %}

 1.2、在`for`循环中,`DTL`提供了一些变量可供使用。这些变量如下:

# `forloop.counter`:当前循环的下标。以1作为起始值。
# `forloop.counter0`:当前循环的下标。以0作为起始值。
# `forloop.revcounter`:当前循环的反向下标值。比如列表有5个元素,那么第一次遍历这个属性是等于5,第二次是4,以此类推。并且是以1作为最后一个元素的下标。
# `forloop.revcounter0`:类似于forloop.revcounter。不同的是最后一个元素的下标是从0开始。
# `forloop.first`:是否是第一次遍历。
# `forloop.last`:是否是最后一次遍历。
# `forloop.parentloop`:如果有多个循环嵌套,那么这个属性代表的是上一级的for循环。

# 需要注意的是:模板中的for...in...没有continue和break语句

 1.3、`for...in...empty`标签

  这个标签使用跟`for...in...`是一样的,只不过是在遍历的对象如果没有元素的情况下,会执行`empty`中的内容。示例代码如下:

# 模板语言

{% for person in persons %}
<li>{{ person }}</li>
{% empty %}
暂时还没有任何人
{% endfor %}

 2、verbatim标签

 `verbatim`标签:默认在`DTL`模板中是会去解析那些特殊字符的。比如`{%`和`%}`以及`{{`等。如果你在某个代码片段中不想使用`DTL`的解析引擎。那么你可以把这个代码片段放在`verbatim`标签中。示例代码下:

# 模板语言

{% verbatim %}
{{if dying}}Still alive.   #{{ if }} 不会被解析,直接原文显示
{% endverbatim %}

3、autoescape标签

  DTL中默认已经开启了自动转义。会将那些特殊字符进行转义。比如会将`<`转义成`&lt;`等,使用autoescape可实现特殊字符手动开启/关闭,示例代码:

{% autoescape off %}  # 关闭自动转义
        {{ info }}
{% endautoescape %}

 

4、spaceless标签

 移除HTML标签中的空白字符,包括空格、tab键、换行等。需要注意的是:spaceless只会移除HTML标签之间的空白字符,而不会移除标签与文本内容之间的空白字符。

 示例代码:

# 原型:
{%  spaceless %}
    <p>
        <a href="#">Foo<a>
    <p>
{%  spaceless %}

# 渲染后:
<p><a href="#">Foo<a><p>

# 注意:如果文本内容"Foo"两边有空白字符,也是不会被移除,而是会跟文本一样保留

 

 


 3、常用模板过滤器 

 3.1、add过滤器

 将传进来的参数添加到原来的值上面。这个过滤器会尝试将`值`和`参数`转换成整形然后进行相加。如果转换成整形过程中失败了,那么会将`值`和`参数`进行拼接。如果是字符串,那么会拼接成字符串,如果是列表,那么会拼接成一个列表。示例代码如下:

{{ value|add:"2" }}

# 如果`value`是等于4,那么结果将是6。如果`value`是等于一个普通的字符串,比如`abc`,那么结果将是`abc2`

 add过滤器的源代码如下:

def add(value, arg):
"""Add the arg to the value."""
try:
return int(value) + int(arg)
except (ValueError, TypeError):
try:
return value + arg
except Exception:
return ''

 

3.2、cut过滤器

  移除值中所有指定的字符串。类似于`python`中的`replace(args,"")`。示例代码如下:

{{ value|cut:" " }}

# 以上示例将会移除`value`中所有的空格字符

 cut过滤器的源代码如下:

def cut(value, arg):
"""Remove all values of arg from the given string."""
safe = isinstance(value, SafeData)
value = value.replace(arg, '')
if safe and arg != ';':
return mark_safe(value)
return value

 3.3、date过滤器

  将一个日期按照指定的格式,格式化成字符串。示例代码如下:

# 数据
context = {
"birthday": datetime.now()
}

# 模版
{{ birthday|date:"Y/m/d" }}

那么将会输出`2018/02/01`。其中`Y`代表的是四位数字的年份,`m`代表的是两位数字的月份,`d`代表的是两位数字的日

 还有更多时间格式化的方式,见下图:

| 格式字符 | 描述 | 示例 |
| --- | --- | --- |
| Y | 四位数字的年份 | 2018 |
| m | 两位数字的月份 | 01-12 |
| n | 月份,1-9前面没有0前缀 | 1-12 |
| d | 两位数字的天 | 01-31 |
| j | 天,但是1-9前面没有0前缀 | 1-31 |
| g | 小时,12小时格式的,1-9前面没有0前缀 | 1-12 |
| h | 小时,12小时格式的,1-9前面有0前缀 | 01-12 |
| G | 小时,24小时格式的,1-9前面没有0前缀 | 1-23 |
| H | 小时,24小时格式的,1-9前面有0前缀 | 01-23 |
| i | 分钟,1-9前面有0前缀 | 00-59 |
| s | 秒,1-9前面有0前缀 | 00-59 |

 

3.4、default过滤器

  如果值被评估为`False`。比如`[]`,`""`,`None`,`{}`等这些在`if`判断中为`False`的值,都会使用`default`过滤器提供的默认值。示例代码如下:

{{ value|default:"nothing" }}
# 如果`value`是等于一个空的字符串。比如`""`,那么以上代码将会输出`nothing`。

 

  * default_if_none

  如果值是`None`,那么将会使用`default_if_none`提供的默认值。这个和`default`有区别,`default`是所有被评估为`False`的都会使用默认值。而`default_if_none`则只有这个值是等于`None`的时候才会使用默认值。示例代码如下:

{{ value|default_if_none:"nothing" }}
# 如果`value`是等于`""`也即空字符串,那么以上会输出空字符串。如果`value`是一个`None`值,以上代码才会输出`nothing`

 

3.5、first过滤器和last过滤器

 first:返回列表/元组/字符串中的第一个元素。示例代码如下:

{{ value|first }}
# 如果`value`是等于`['a','b','c']`,那么输出将会是`a`

 last:返回列表/元组/字符串中的最后一个元素。示例代码如下:

{{ value|last }}
# 如果`value`是等于`['a','b','c']`,那么输出将会是`c`

 

3.6、floatformat过滤器

 使用四舍五入的方式格式化一个浮点类型。如果这个过滤器没有传递任何参数。那么只会在小数点后保留一个小数,如果小数后面全是0,那么只会保留整数。当然也可以传递一个参数,标识具体要保留几个小数。

 1)如果没有传递参数:  

| value | 模版代码 | 输出 |
| --- | --- | --- |
| 34.23234 | `{{ value|floatformat }}` | 34.2 |
| 34.000 | `{{ value|floatformat }}` | 34 |
| 34.260 | `{{ value|floatformat }}` | 34.3 |

 2)如果传递参数,按指定的参数保留小数位:

| value | 模版代码 | 输出 |
| --- | --- | --- |
| 34.23234 | `{{value|floatformat:3}}` | 34.232 |
| 34.0000 | `{{value|floatformat:3}}` | 34.000 |
| 34.26000 | `{{value|floatformat:3}}` | 34.260 |

 

3.7、join过滤器

 类似与`Python`中的`join`,将列表/元组/字符串用指定的字符进行拼接。示例代码如下:

{{ value|join:"/" }}
# 如果`value`是等于`['a','b','c']`,那么以上代码将输出`a/b/c`。

3.8、length过滤器

 获取一个列表/元组/字符串/字典的长度。示例代码如下:

{{ value|length }}
# 如果`value`是等于`['a','b','c']`,那么以上代码将输出`3`。如果`value`为`None`,那么以上将返回`0`

 

3.9、lower过滤器和upper过滤器

 lower:将值中所有的字符全部转换成小写。示例代码如下:

{{ value|lower }}
# 如果`value`是等于`Hello World`。那么以上代码将输出`hello world`

 upper:类似于`lower`,只不过是将指定的字符串全部转换成大写

3.10、random过滤器

 在被给的列表/字符串/元组中随机的选择一个值。示例代码如下:

{{ value|random }}
# 如果`value`是等于`['a','b','c']`,那么以上代码会在列表中随机选择一个

 

3.11、safe过滤器

 标记一个字符串是安全的。也即会关掉这个字符串的自动转义。示例代码如下:

{{value|safe}}

# 如果`value`是一个不包含任何特殊字符的字符串,比如`<a>`这种,那么以上代码就会把字符串正常的输入。如果`value`是一串`html`代码,那么以上代码将会把这个`html`代码渲染到浏览器中

 

3.12、slice过滤器

 类似于`Python`中的切片操作。示例代码如下:

{{ some_list|slice:"2:" }}

# 以上代码将会给`some_list`从`2`开始做切片操作,保留索引'2'往后的数据

 

3.13、striptags过滤器

 删除字符串中所有的`html`标签。示例代码如下:

{{ value|striptags }}

# 如果`value`是`<strong>hello world</strong>`,那么以上代码将会输出`hello world`

 

3.14、truncatechars过滤器

  如果给定的字符串长度超过了过滤器指定的长度。那么就会进行切割,并且会拼接三个点来作为省略号。示例代码如下:

{{ value|truncatechars:5 }}

# 如果`value`是等于`北京欢迎您~`,那么输出的结果是`北京...`。可能你会想,为什么不会`北京欢迎您...`呢。因为三个点也占了三个字符,所以`北京`+三个点的字符长度就是5

 

3.15、truncatechars_html过滤器

 类似于`truncatechars`,只不过是不会切割`html`标签。示例代码如下:

{{ value|truncatechars_html:5 }}

# 如果`value`是等于`<p>北京欢迎您~</p>`,那么输出将是`<p>北京...</p>`

 


 

3.16、自定义模板过滤器

步骤:

  1. 首先在某个app中,创建一个python包,叫做`templatetags`,注意,这个包的名字必须为`templatetags`,不然就找不到。
  2. 在这个`templatetags`包下面,创建一个python文件用来存储过滤器,命名可自定义。
  3. 在新建的python文件中,定义过滤器(也就是函数),这个函数的第一个参数永远是被过滤的那个值,并且如果在使用过滤器的时候传递参数,那么还可以定义另外一个参数。但是过滤器最多只能有2个参数。
  4. 在写完过滤器(函数)后,要使用`django.template.Library.filter`进行注册。
  5. 还要把这个过滤器所在的这个app添加到`settings.INSTALLED_APS`中,不然Django也找不到这个过滤器。
  6. 在模板中使用`load`标签加载过滤器所在的python包。

* `django.template.Library.filter`还可以当作装饰器来使用。如果`filter`函数没有传递任何参数,那么将会使用这个函数的名字来作为过滤器的名字。当然如果你不想使用函数的名字来作为过滤器的名字,也可以传递一个`name`参数。示例代码如下:
```
@register.filter('my_greet')
def greet(value,word):
return value + word
```

templatetags/my_filter.py:

from django import template

register = template.Library()

# 过滤器最多只能有两个参数
# 过滤器的第一个参数永远都是被过滤的那个参数(也就是竖线左边的那个参数)def greet(value,word):
    return value + word

register.filter("greet",greet) # 注册过滤器

# @register.filter('my_greet')
# def greet(value,word):
    # return value + word

 

 HTML:

<body>
    {{ value|my_greet:" 你好" }}
    {{ mytime|time_since }}
</body>

 * 自定义过滤模板实例

 templatetags/my_filter.py:

from django import template
from datetime import datetime

register = template.Library()


@register.filter
def time_since(value):
    """
    需求:
    time距离现在的时间间隔
    1. 如果时间间隔小于1分钟以内,那么就显示“刚刚”
    2. 如果是大于1分钟小于1小时,那么就显示“xx分钟前”
    3. 如果是大于1小时小于24小时,那么就显示“xx小时前”
    4. 如果是大于24小时小于30天以内,那么就显示“xx天前”
    5. 否则就是显示具体的时间
    6. 2017/10/20 16:15
    """
    if not isinstance(value,datetime):
        return value
    now = datetime.now()
    # timedelay.total_seconds:将时间换算成秒数
    # timestamp是timedelay类型
    timestamp = (now - value).total_seconds()
    if timestamp < 60: # 小于60秒
        return '刚刚'
    elif timestamp >= 60 and timestamp < 60*60:  # 小于60分钟
        minutes = int(timestamp/60)
        return '%s分钟前' % minutes
    elif timestamp >= 60*60 and timestamp < 60*60*24:  # 小于24小时
        hours = int(timestamp/60/60)
        return '%s小时前' % hours
    elif timestamp >= 60*60*24 and timestamp < 60*60*24*30:  # 小于30天
        days = int(timestamp/60/60/24)
        return '%s天前' % days
    else:
        return value.strftime("%Y/%m/%d %H:%M")
demo

 HTML模板:

{% load my_filter %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    {{ mytime|time_since }}
</body>
</html>
html

 3.17、static静态文件设置拾遗

  如果不想每次在模版中加载静态文件都使用`load`加载`static`标签,那么可以在`settings.py`中的`TEMPLATES/OPTIONS`添加`'builtins':['django.templatetags.static']`,

这样以后在模版中就可以直接使用`static`标签,而不用手动的`load`了。具体配置:

settings.py:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
            'builtins': ['django.templatetags.static']  # 配置此一行代码
        },
    },
]
settings.py

 

 三、数据库操作拾遗

 1、MySQL常见驱动:

 2、外键删除操作:

 3、查询条件笔记

3.1、exact 和 iexact

 exact:在底层会被翻译成'='

 iexact:在底层会被翻译成'like'

 (iexact)like 跟 (exact)= 在大多数情况下是等价的,只有少数情况下不等价,如:like '%hello%'(即模糊匹配的时候,两边包含可任意匹配字符)

 

3.2、在view视图函数中查看ORM执行语句的底层实现方法,即原始的数据库语句操作,可以用QuerySet.query方法来执行,即对象type是querysets类型,则可以通过query方法来获取/打印底层相关的数据库语句操作

 

3.3、contains 和 icontains

 模糊匹配

 contains:使用大小写敏感的判断,在底层被翻译成SQL语句时,会使用'like binary'查找,即大小写敏感查找

 icontains:使用大小写不敏感的判断,在底层被翻译成SQL语句时,使用的是like,在MySQL层面中就是表示不区分大小写的

 

3.4、in

 包含查询

 

3.5、related_name 和 related_query_name

 这两个不是用于当查询条件的,是用于model模型中取别名的

 在一对多中:正向表是多(存有反向表的id),反向表是一(没有存正向表的相关数据)

  正向查询关联表的数据,可以通过关联表(反向表)的表名小写直接查询;也可以通过关联表的小写表名+ '_id'的方式查找关联表的主键id(正向表中实际存有关联表的id:小写表名_id)

  反向查询时,需要正向表小写表名_set的方式查询;在过滤(即条件查询)中,如果要反向查关联表(正向表)数据,可以通过关联表小写表名__id(双下划线)查询id的方式来查找

  如果觉得上述规则麻烦,此时就可以使用related_name来简单化处理,related_name在model模型中的外键(models.ForeignKey)使用,使用related_name指定表名,这样在反向查询关联表数据时,就可以直接用related_name指定

 的name来查询,而不需要再通过正向表小写表名_set的方式去查询

 另外,related_query_name也可以在model模型中指定表名,但只起到替换默认表名的作用,比如model中新建一个类,类名(默认表名):user,假如此时使用related_query_name='User',之后使用查询相关时便不再是user_set、user__id,而是:User_set、User__id

 3.5.1、一对一、一对多、多对多反向查询:

一对一: request.user.userprofile  # request.user是当前对象,后面跟反向的小写表名(userprofile),得到的是单一对象

一对多: request.user.userprofile_set.all()  # 后面跟反向的小写表名+ _set(userprofile_set),得到的是个querysets集合

多对多:request.user.role_set.all    # 同一对多反向查询,得到的是个querysets集合。正向操作与一对多一样,也是直接跟小写表名

  3.5.2、一对一、一对多、多对多过滤查询

  都是使用小写表名+双下划线+字段名的形式。即:"表名__字段名"

 

3.6、gt、gte、lt、lte:代表的是大于、大于等于、小于、小于等于的条件

 

3.7、startswith、istartswith、endswith、iendswith:分别表示:以某个值开始、不区分大小写的以某个值开始、以某个值结束、不区分大小写的以某个值结束

 

3.8、关于时间的查询条件

 1)range:可以指定一个时间段。并且时间应该标记为`aware`时间,不能为navie,为navie时django会报警告。示例:

from django.utils.timezone import make_aware

start_time = make_aware(datetime(year=2018,month=4,day=4,hour=17,minute=0,second=0))
        end_time = make_aware(datetime(year=2018,month=4,day=4,hour=18,minute=0,second=0))
        articles = Article.objects.filter(create_time__range=(start_time,end_time))  # 查找创建时间在某时间段内的所有article数据
        print(articles.query)
        print(articles)

 其中,make_aware方法表示,把navie(幼稚的)时间,转换成aware(成熟的)时间。datetime得到的是navie时间

 关于日期的查询,本文暂时不涉及

 

3.9、isnull

 根据值是否为空进行查找。示例代码:

article = Article.objects.filter(pub_date__isnull=False)

# 如果是反向表查询,则需:pub__date__isnull

 

3.10、regex 和 iregex

 大小写敏感和大小写不敏感的正则表达式。示例代码:

article = Article.objects.filter(title__regex=r'^hello')  # 提取文章中所有标题以hello字符串开头的文章

 

3.11、excute

 不等于、不包含于。demo:

User.objects.filter().excute(age=10)    # 查询年龄不为10的用户
User.objects.filter().excute(age__in=[10, 20])  # 查询年龄不为在 [10, 20] 的用户

 

3.12、ORM F表达式(重要)

 F表达式是用来优化ORM 操作数据库的。比如我们要将公司所有员工的薪水都增加1000元,如果按照正常的流程,应该是先从数据库中提取所有的员工工资到python内存中,然后使用python代码在员工工资的基础上增加1000元,最后保存到数据库。示例代码:

employees = Employee.objects.all()
for employee in employees:
    employee.salary += 1000
    employee.save()

 

而我们的F表达式 就可以优化这个流程,它可以不需要将数据从数据库中提取出来,计算完成后再保存回去。它可以直接执行SQL语句,直接作用于数据库,而python中不需要知道操作中的具体值。示例代码: 

from django.db.models import F
Employee.objects.update(salary=F("salary")+1000)
# 结果一致

 

F表达式 并不会马上从数据库中获取数据,而是在生成SQL语句时,动态的获取传给F表达式的值。比如如果想要获取作者中,name和email相同的作者数据,如果不使用F表达式,那么需要使用以下代码实现:

authors = Author.object.all()
for author in authors:
    if author.name == author.email:
        print(author)

 

如果使用 F表达式, 那么一行代码就可以搞定:

from django.db.models import F

authors = Author.objects.filter(name=F("email"))

Django支持F()对象使用加、减、乘、除、取模和幂运算等算术操作,两个操作数可以是常数或F()对象。demo:

Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)

Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))

你还可以在F()对象中使用双下划线标记来跨越关联关系。 带有双下划线的F()对象将引入任何需要的join 操作以访问关联的对象。 例如,如要获取author 的名字与blog 名字相同的Entry,我们可以这样查询:

Entry.objects.filter(authors__name=F('blog__name'))

对于date 和date/time 字段,你可以给它们加上或减去一个timedelta对象。 下面的例子将返回发布超过3天后被修改的所有Entry:

from datetime import timedelta
Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))

 

 3.13、Q表达式(重要)

 略


 

 4、QuerySet API

 关于通过ORM获取到的三种QuerySet类型:

obj_list = models.User.objects.all()    # QuerySet类型

dict_list = models.User.objects.all().values()    # 字典类型(最外层是QuerySet)

data_list = models.User.objects.all().values_list()    #
 列表类型(最外层是QuerySet)

关于python序列化QuerySet类型:

当数据是QuerySet类型时,使用Django自带serializers模块,转化成json格式的字符串:ret[data]=serializers.serialize('json',obj_list) ,
然后在通过HTTPResponse渲染返回到前端:return HttpResponse(json.dumps(data),content_type="application/json") 

当数据是dict_list(字典类型)、data_list(列表类型)时,只需要将数据用括号包裹起来:ret[data]=(dict_list),即可以用json.dumps()进行序列化处理: json.dumps(ret)

 4.1、QuerySet对象的方法介绍:

 1)filter:将满足条件的数据提取出来,返回一个新的QuerySet

 2)exclude:排除满足条件的数据,返回一个新的QuerySet

 3)annotate:给QuerySet中的每个对象都添加一个使用查询表达式(如:聚合函数、F表达式、Q表达式、Func表达式等)的新字段。示例:

arti = Article.objects.annotate(author_name=F("author_name"))
# 在每个对象中都添加一个 author_name 字段,用来显示这个文章的作者的名字

 4)order_by:排序

  注意:如果同个语句有多个排序,则会把前面排序的规则给打乱,而使用后面的排序方式

 5)value:用来提取数据时,指定需要提取的字段。默认提取全部字段,也可以提取指定字段。这种方式提取出来的数据类型不是模型,而是字典形式的QuerySet,上面有讲

 6)values_list:类似于values,只不过返回的是列表,包含元组,外层是QuerySet,上面有讲。demo:

article = Article.objects.values_list("id","title")  # 提取指定字段数据

上述操作后,结果为:<QuerySet [(1,'abc),(2,'xxx'),...]>

如果在values_list中只有一个字段,那么可以传递 flat = True 来将结果扁平化,示例如下:

article = Article.objects.values_list("title",flat=True)

# 运行结果:<QuerySet ["abc","xxx",...]>

 7)all:获取这个ORM模型的QuerySet对象

 8)select_related: 在提取某个模型的数据的同时,也提前将相关联的数据提取出来。比如提取文章数据时,可以将关联表相关的author信息提取出来,以后再次使用article.author时就不需要再次去访问数据库了。可以减少数据库查询的次数。示例代码:

article = Article.objects.select_ralated("author").get(pk=2)
# 相关的author信息会被提取出来,再次查询author时不需要再访问数据库

 注意:select_related 只能在一对多 或者一对一中使用,不能再多对多或多对一中使用。比如可以提起获取文章的作者,但是不能通过作者获取这个作者的文章,或者是通过某篇文章获取这篇文章所有的标签。

 9)prefetch_related:这个方法和 select_related类似,就是在访问多个表中的数据的时候,减少查询的次数。这个方法是为了解决 多对一 和 多对多 的关系的查询问题。比如要获取标题中带有hello 字符串的文章以及它的所有标签。 

 10)defer:在一些表中,可能存在很多的字段,但是一些字段的数据量可能会比较庞大的,而此时你又不需要这么多数据,比如我们在获取文章列表的时候,文章的内容我们是不需要的,因此这时候我们就可以使用defer来过滤掉一些字段。这个字段跟value有点类似,只不过defer返回的不是字典,而是模型。示例代码:

articles = list(Article.objects.defer("title"))
for sql in connection.queries:
    print('='*30)
    print(sql)

# 运行结果,除了title字段相关的数据外,其他字段的数据都会查找出来

注意:defer虽然能过滤字段,但是有些字段是不能过滤的,比如id,即使过滤了,也会被提取出来。

 11)only:跟defer类似,只不过defer 是过滤掉指定的字段, 而only 是只提取指定的字段

 12)get:获取满足条件的数据。这个函数只能返回一条数据,并且如果给的条件有多条数据,那么这个方法会抛出MultipleObjectReturned错误, 如果给的条件没有任何数据,那么会抛出DoesNoeExit错误。

 13)create:创建一条数据,并且保存到数据库中

 14)get_or_create:根据某个条件进行查找,如果找到了那么返回这条数据,如果没有查找到,那么久创建一个。这个方法的返回值是一个元组,元组的第一个参数obj是这个对象,第二个参数created代表是否创建的

 15)bulk_create:一次性创建多个数据。示例代码:

Tag.objects.bulk_create(
    [
        Tag(name='111'),
        Tag(name='222'),
    ]
)

 16)count:获取提取的数据的个数。

 17)first 和 last:返回QuerySet中的第一条和最后一条数据

 18)aggregate:使用聚合函数

 19)exists:判断某个条件的数据是否存在。如果要判断某个条件的元素是否存在,那么建议使用exists,这比使用count 或者直接判断 QuerySet 更有效的多。

 20)distinct:去除掉那么重复的数据。示例代码:

books = Book.objects.filter(bookorder__price__gte=80).distinct()

需要注意的是,如果在distinct之前使用了order_by, 那么因为order_by会提取order_by中指定的字段,因此再使用distinct就会根据多个字段来进行唯一化,所以就不会把那些重复的数据删掉。 

 22)update:执行更新操作。

 23)delete:删除操作。删除数据时,需要注意on_delete指定的处理方式


 5、makemigrations 和 migrate

 5.1、makemigrations:将模型生成迁移脚本。

 --name:给这个迁移脚本指定一个名字

 --empty:生成一个空的迁移脚本。如果你想写自己的迁移脚本,可以使用这个命令来实现一个空的文件,然后自己再在文件中写迁移脚本

 5.2、migrate:将新生成的迁移脚本,映射到数据库中。一般是创建新的表或者修改表的结构

 --fake:可以将指定的迁移脚本名字添加到数据库中,但是并不会把迁移脚本转换成SQL语句,即不会执行SQL语句

 --fake-initial:将第一次生成的迁移文件版本号记录到数据库中,但不会真正的执行迁移脚本SQL语句

 5.3、showmigrate:查看某个app下的迁移文件。如果后面没带app名称,那么将查看INSTALLED_APPS 中的所有迁移文件

 5.4、sqlmigrate:查看某个迁移文件在映射到数据库中的时候,转换的SQL语句

 

*** 数据库迁移问题

 1)migrate怎么判断哪些迁移脚本需要执行:

  他会将代码中的迁移脚本和数据库中`django_migrations`中的迁移脚本进行对比,如果发现数据库中,没有这个迁移脚本,那么就会执行这个迁移脚本

 2)migrate做了什么事情:

  1. 将相关的迁移脚本翻译成SQL语句,在数据库中执行这个SQL语句。

  2. 如果这个SQL语句执行没有问题,那么就会将这个迁移脚本的名字记录到`django_migrations`中

 3)执行migrate命令的时候报错的解决办法:

  执行migrate命令会报错的原因是:数据库的`django_migrations`表中的迁移版本记录和代码中的迁移脚本不一致导致的

 

 普通解决方法:使用 --fake参数

  首先对比数据库中的迁移脚本和代码中的迁移脚本。然后找到哪个不同,之后再使用`--fake`,将代码中的迁移脚本添加到`django_migrations`中,但是并不会执行sql语句。这样就可以避免每次执行`migrate`的时候,都执行一些重复的迁移脚本。

 终极解决方案:

  如果代码中的迁移脚本和数据库中的迁移脚本实在太多,就是搞不清了。那么这时候就可以直接使用以下终极解决方案:

   终极解决方案原理:就是将之前的那些迁移脚本都不用了(删除)。重新来过。要将出问题的app下的所有模型和数据库中表保持一致,重新映射

   1. 修改出问题的app下的所有模型,让model中数据都和数据库中的表保持一致

   2. 将出问题的app下的所有迁移脚本文件都删掉。再在`django_migrations`表中将出问题的app相关的迁移记录都删掉

   3. 使用`makemigrations`,重新将模型生成一个迁移脚本

   4. 使用`migrate --fake-initial`参数,将刚刚生成的迁移脚本,标记为已经完成(因为这些模型相对应的表,其实都已经在数据库中存在了,不需要重复执行了)

  

5.5、根据已有的表自动生成模型

 在实际开发中,有些时候可能数据库已经存在,而model表还没有。比如使用java开发的网站,已经存在相应的数据库数据,此时我们想更改语言使用python-django来开发网站,但数据库还是使用原先的数据库。此时我们就需要根据已有的数据库表来自动生成我们的django model 模型

 Django 给我们提供了一个 inspectdb 的命令,可以非常方便的将已经存在的表,自动的生成model模型:python manage.py inspectdb

 上述命令是在终端执行的,不能在 pycharm -> Tools -> Run manage.py Task...中执行。如果想要保存到文件中,比如保存到Django models.py中,可以使用 > 重定向输出倒指定文件:

  python manage.py inspectdb > models.py


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

posted on 2019-03-19 17:59  Eric_nan  阅读(878)  评论(0编辑  收藏  举报