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中默认已经开启了自动转义。会将那些特殊字符进行转义。比如会将`<`转义成`<`等,使用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、自定义模板过滤器
步骤:
- 首先在某个app中,创建一个python包,叫做`templatetags`,注意,这个包的名字必须为`templatetags`,不然就找不到。
- 在这个`templatetags`包下面,创建一个python文件用来存储过滤器,命名可自定义。
- 在新建的python文件中,定义过滤器(也就是函数),这个函数的第一个参数永远是被过滤的那个值,并且如果在使用过滤器的时候传递参数,那么还可以定义另外一个参数。但是过滤器最多只能有2个参数。
- 在写完过滤器(函数)后,要使用`django.template.Library.filter`进行注册。
- 还要把这个过滤器所在的这个app添加到`settings.INSTALLED_APS`中,不然Django也找不到这个过滤器。
- 在模板中使用`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")
HTML模板:
{% load my_filter %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ mytime|time_since }} </body> </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'] # 配置此一行代码 }, }, ]
三、数据库操作拾遗
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