首页 > 其他分享 >第十周总结

第十周总结

时间:2022-12-18 22:35:29浏览次数:33  
标签:总结 name 第十 models res filter objects print

Django请求生命周期流程图

1.路由层、视图层、模板层、模型层、组件、BBS项目

2.django路由层

1.路由匹配:
'''当我们输入一个地址时,接口后面的/不用输入,也可以自动跳转'''
	django2.X以上版本,path第一个参数末尾可以不加/,django也会自动补全该接口。
	django1.X版本:第一个参数是正则表达式,无法补全。
	如果想取消django2.X以上版本的自动补全功能,需要在settings.py中加入:APPEND_SLASH = False。

2.转换器:
'''正常情况下很多网站都会有很多相似的网址,如果我们每一个都单独开发开设路由不合理'''
	2.1当我们想在接口后面继续增加一个后缀时,我们可以在接口后面加上/<str:info>/,比如:path('index/<str:a>/', views.index)表示index/后面两个/之间输入任意字符串内容,该地址访问到的还是/index/地址,并且该字符串还能被index()函数接收到。index函数有两个参数:index(request,a),这个a表示的就是index/后面接收到的字符串(可以带数字)。
	2.2/<int:num>/:匹配0或者任意正整数。
	2.3其他匹配内容:
			slug:匹配任意一个由字母或数字组成的字符串。
			uuid:匹配格式化后的UUID。
			path:能够匹配完整的URL路径
			ps:还支持自定义转换器(自己写正则表达式匹配更加细化的内容)
            
3.正则匹配:
	django2.X以上版本可以通过正则来匹配,需要提前导入模块re_path,第一个参数是正则。当我们在urls中写入一个接口,第一个参数只要能通过正则匹配到地址栏输入的接口,那么就可以匹配到该接口对应的功能能。
"""
但是不加任何符号匹配也有弊端,比如在urls中分别定义一个'test'和'testadd'的接口(test接口在上),那么我们能在地址栏输入/testadd/时,通过正则从上到下首先匹配到的是'test',所以我们匹配到的是test对应的功能,然后结束路由层的匹配,直接执行视图函数。想要解决该问题可以将接口改为re_path('test/', views.test)。或者是加上正则筛选条件:
urlpatterns = [
    re_path('^test/$', views.test),
    re_path('^testadd/$',views.testadd)
]
"""
4.正则匹配的无名有名分组:
	4.1无名分组:re_path('test/(\d{4})/',views.test),会将括号内的正则匹配到的内容当做位置参数传递给视图函数。
	4.2有名分组:re_path('func/(?P<num>\d{4})/(?P<var>\d{4})',views.function),会将括号内的正则匹配到的内容当做关键字参数传递给视图函数,所以视图函数在定义形参时需要将request以外的形参定义成<>内的名字。
'''无名、有名分组不能混用'''

反向解析

反向解析:在a标签中,如果一个路由经常变化,那么我们可以通过一个名字反向解析出一个结果,该结果可以直接访问到某个对应的路由,而不通过路由访问。
    
1.路由关系起别名基本语法:
path('login/', views.login, name='login_view')
<a href="{% url 'login_view' %}">home界面</a>  # 点击home界面就可以直接访问到login/路由对应的功能

2.反向解析语法:
urls语法:path('login001/', views.login, name='login_view')
html页面上模板语法:<a href="{% url 'login_view' %}">home界面</a>
后端语法:reverse('login_view')
    
3.动态路由的反向解析
path('func1/<str:others>/', views.func1_func, name='func1_view')
html页面上模板语法:{% url 'func1_view' 'jason' %}
后端语法:reverse('func1_view', args=('嘿嘿嘿',))

路由分发

1.Django当中很多业务不只有一个应用,Django支持每个应用都可以有自己独立的路由层(urls)、静态文件、模板层。基于该特性多人开发项目就可以完全解耦合,之后利用路由分发还可以整合到一起。

2.在每个应用下建一个urls.py,该路由层只负责和本应用下的views中的功能连接。而总的urls负责和各个应用的urls连接。这样我们在访问地址时需要指明是哪个app,然后就可以访问到不同app下的功能。

3.此后我们访问app下的路由名就变成了:应用名/路由/。

app01:
	urls:
urlpatterns = [
    path('func/', views.func)
]

	views:
def func(request):
    return HttpResponse('from app01 func')

app02:
	urls:
urlpatterns = [
    path('func/', views.func)
]
    
	views:
def func(request):
    return HttpResponse('from app02 func')

总urls:
urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls')),
    path('app02/', include('app02.urls'))
]

名称空间

1.如果我们在路由分发之后,需要在不同的app下面有相同的别名,可以通过名称空间反向解析出不同的应用前缀。
如果想要正常识别区分有两种方式
	方式1:名称空间
 		总路由
    path('app01/', include(('app01.urls', 'app01'), namespace='app01')),
    path('app02/', include(('app02.urls', 'app02'), namespace='app02')),
    	反向解析
        reverse('app01:index_view')
        reverse('app02:index_view')
        
2.上述过程较为繁琐,我们可以直接在别名的前面加上app的名字。
app中urls:
urlpatterns = [
    path('func/', views.func,name='app02_func_view')
]

app中views:
def func(request):
    print(reverse('app02_func_view'))
    return HttpResponse('from app02 func')

虚拟环境

1.在实际项目中,我们可能需要使用不同的django版本,因为下载模块需要消耗资源,为了节省资源,只给需要的项目配备适当的模块,所以我们需要使用虚拟环境。虚拟环境相当于一个解释的多个分身,每个解释器可以实现多个虚拟环境共存。每个分身可以有自己独立的环境。

2.pycharm创建虚拟环境:每创建一个虚拟环境就相当于重新下载了一个全新的解释器。
    
3.命令行的方式: python -m venv pyvenv38
    
4.在cmd窗口中将路径切换到Scripts目录下,输入dir查看所有文件。输入activate激活虚拟环境,输入deactivate关闭虚拟环境。当左侧出现虚拟文件目录名的时候说明虚拟环境已经激活。
"""
注意:python命令此处不支持多版本共存的操作,在命令行创建在python后面不支持写版本号,在环境变量中哪个解释器靠前就选择哪个解释器。想使用哪个解释器需要在解释器中把改路径排在前面。
"""

视图层之必会三板斧

"""
用来处理请求的视图函数都必须返回HttpResponse对象:完全正确。因为。
"""

	
1.查看源码我们得知,render功能的返回值是一个HttpResponse对象,说明它们都是有HttpResponse对象演变而来的
def render():
	return HttpResponse()

2.redirect继承的不是HttpResponse,但是祖先有个类是HttpResponse,所以redirect对象也是HttpResponse对象。
def redirect(to, *args, permanent=False, **kwargs):
	redirect_class = 类(祖先有个类是HttpResponse)
	return redirect_class(resolve_url(to, *args, **kwargs))
"""
综上所述:用来处理请求的视图函数都必须返回HttpResponse对象。
"""

视图层之JsonResponse对象

1.JsonResponse对象可以将后端的字段数据直接变成Json格式在页面显示。
模块导入:from django.http import JsonResponse
    
views:    
def register(request):
    userdict = {'name':'max', 'age':25}
    return JsonResponse(userdict)
	
2.但是当我们传入中文之后,发现中文依旧会乱码。
"""
在json.dump()中有一个参数是ensure_ascii=Flase,这个参数可以控制中文是否编码。我们查看JsonResponse的源码:
    def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
                 json_dumps_params=None, **kwargs):
        if safe and not isinstance(data, dict):
            raise TypeError(
                'In order to allow non-dict objects to be serialized set the '
                'safe parameter to False.'
            )
        if json_dumps_params is None:
            json_dumps_params = {}
        kwargs.setdefault('content_type', 'application/json')
        data = json.dumps(data, cls=encoder, **json_dumps_params)
        super().__init__(content=data, **kwargs)
我们发现json.dumps(data, cls=encoder, **json_dumps_params)中有一个参数json_dumps_params是字典类型,并且前面带**说明可以将字典打散成关键字参数。所以我们直接给json_dumps_params字典指定一个值{'ensure_ascii':False},**会将字典打散成关键字参数传入。return JsonResponse(userdict,json_dumps_params={'ensure_ascii':False})
"""

3.当我们想要将其他数据类型序列化转世在页面上,依然可以通过看源码的方式得到:需要加一个参数safe=False。
"""
源码:
def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
                 json_dumps_params=None, **kwargs):
        if safe and not isinstance(data, dict): 
            raise TypeError(
                'In order to allow non-dict objects to be serialized set the'safe parameter to False.'
            ) 
"""
   
def login(request):
    userlist = [1, 'jason', 2, 'max', (1,2,3)]
    return JsonResponse(userlist,safe=False)

视图层之request对象获取文件

form表单携带文件类型必须要做到以下几点:
"""
当我们输入以下代码,并且通过request.POST拿到用户数据字典之后,我们发现文件的name对应的是一个文件名:
	1.method表单必须是post(因为get)。
	2.form标签中enctype对应的值必须是multipart/form-data。
	3.django后端需要通过request.FILE获取文件类型的数据。
获取到的是一个文件对象:  
"""
我们可以通过文件读写的方式,让获取到的文件保存到指定位置:
views:
def info(request):
    if request.method == 'POST':
        print(request.POST)
        print(request.FILES)
        file_obj = request.FILES.get('file')
        print(file_obj.name)
        with open(r'%s'%file_obj.name, 'wb') as f:
            for line in file_obj:
                f.write(line)
    return render(request,'index.html')

视图层之FBV与CBV

1.FBV:基于函数的视图。
def index(request):
	return HttpResponse('字符串')

2.CBV:基于类的视图,用面向对象报的语法来写视图层功能。
views:
from django import views


class MyLoginView(views.View):
    def get(self,request):
        return HttpResponse('from CBV get function')

    def post(self,request):
        return HttpResponse('from CBV post function')
    
urls:
urlpatterns = [
    path('login1/', views.MyLoginView.as_view())
]
会自动根据请求方法的不同自动匹配对应的方法并执行:
当我们访问login1/路由时,会向服务端MyLoginView发送一个get请求,因为类中有get方法,所以会直接返回HttpResponse('from CBV get function')。
当我们访问其他页面时,向login1/路由提交数据,会触发类中post方法,返回HttpResponse('from CBV post function')。
5.CBV源码剖析(重要)
1.我们想要查看CBV源码,从路由层入手:
urls:
urlpatterns = [
    path('login1/', views.MyLoginView.as_view()),
]

views:
class MyLoginView(views.View):
    def get(self,request):
        return HttpResponse('from CBV get function')

    def post(self,request):
        return HttpResponse('from CBV post function')
MyLoginView是一个类,类名点一个名字首先去类中查找,但是类MyLoginView中没有as_view名字,并且该名字还加了括号。再去父类views.View中查找。

2.在父类View中,有一个绑定给类的as_view方法,并且函数名加括号,优先级最高:


并且在as_view方法中,还有一个view函数,并且该函数的返回值是view,所以
 views.MyLoginView.as_view()会变形称为:views.view。此时
urlpatterns = [
    path('login1/', views.view),
]
类似于FBV中的方法,所以CBV的本质还是FBV。



3.cls是我们定义的类MyLoginView,所以self=cls(),self是类MyLoginView的一个对象。view函数的返回之是:return self.dispatch(request, *args, **kwargs)。self是一个对象,对象点名字首先要在对象名称空间中查找,我们暂未给该对象添加任何名字,找不到,然后再到类名称空间中查找,类中只有get和post两个名字。所以继续到父类中查找,发现有dispatch函数(如果我们在MyLoginView中定义一个dispatch方法就会优先查找到类中的dispatch):


4.dispatch函数体代码:
    def dispatch(self, request, *args, **kwargs):
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)
我们发现分支结构中的if:request.method.lower()就是我们的GET/POST请求,lower()转为小写。http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']。显然request.method.lower()在http_method_names中。所以走if分支。if分支中:类中有post/get方法,所以反射拿到一个post/get的结果并且返回。

CBV源码剖析(重要)

1.我们想要查看CBV源码,从路由层入手:
urls:
urlpatterns = [
    path('login1/', views.MyLoginView.as_view()),
]

views:
class MyLoginView(views.View):
    def get(self,request):
        return HttpResponse('from CBV get function')

    def post(self,request):
        return HttpResponse('from CBV post function')
MyLoginView是一个类,类名点一个名字首先去类中查找,但是类MyLoginView中没有as_view名字,并且该名字还加了括号。再去父类views.View中查找。

2.在父类View中,有一个绑定给类的as_view方法,并且函数名加括号,优先级最高。
并且在as_view方法中,还有一个view函数,并且该函数的返回值是view,所以
 views.MyLoginView.as_view()会变形称为:views.view。此时
urlpatterns = [
    path('login1/', views.view),
]
类似于FBV中的方法,所以CBV的本质还是FBV。

3.cls是我们定义的类MyLoginView,所以self=cls(),self是类MyLoginView的一个对象。view函数的返回之是:return self.dispatch(request, *args, **kwargs)。self是一个对象,对象点名字首先要在对象名称空间中查找,我们暂未给该对象添加任何名字,找不到,然后再到类名称空间中查找,类中只有get和post两个名字。所以继续到父类中查找,发现有dispatch函数(如果我们在MyLoginView中定义一个dispatch方法就会优先查找到类中的dispatch)。

4.4.dispatch函数体代码:
    def dispatch(self, request, *args, **kwargs):
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)
我们发现分支结构中的if:request.method.lower()就是我们的GET/POST请求,lower()转为小写。http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']。显然request.method.lower()在http_method_names中。所以走if分支。if分支中:类中有post/get方法,所以反射拿到一个post/get的结果并且返回。

模板层传值

方式1:在views重定义函数时,将函数体代码里的名字用字典的键一一对应,将该字典当做render()的第三个参数传给html文件。在html文件中通过{{键名}}的方式拿到views中的名字。
views:
def func1(request):
    name = 'max'
    age = 25
    hobby = ['soccer','read','run']
    return render(request,'func1.html', {'d1':name,'d2':age,'d3':hobby})

func1.html:
<body>
    {{ d1 }}
    {{ d2 }}
    {{ d3 }}
</body>

方式二:在views中的功能函数中将locals()当做render的第三个参数传递给html文件中,在html文件中直接拿到views中对应函数的名字。
views:
def func1(request):
    name = 'max'
    age = 25
    hobby = ['soccer','read','run']
    return render(request,'func1.html', locals())

func1.html:
<body>
    {{ name }}
    {{ age }}
    {{ hobby }}
</body>

模板层之标签

1.模板层if条件判断:
{% if 条件1(可以自己写也可以用视图层传递过来的数据) %}
	执行的条件
{% elif 条件2(可以自己写也可以用视图层传递过来的数据) %}
	执行的条件
{% else %}
	执行的条件
{% endif %}
'''可以只有if/else分支,elif可有可无'''、
views.py:
def index_func(request):
    a1 = 1
    a2 = 0
    return render(request,'indexPage.html', locals())

indexPage.html:
<body>
    {% if a1 %}
        <p>if成立打印的语句</p>
    {% else %}
        <p>if不成立执行的语句</p>
    {% endif %}
</body>
# 执行结果:if成立打印的语句

2.模板层for循环:
	2.1基本与方法举例说明:
views.py:
def index_func(request):
    l1 = [1, 2, 3, 4, 5]
    return render(request, 'indexPage.html', locals())    
    
indexPage.html:
<body>
    {% for l in l1 %}
        <p>{{ l }}</p>
    {% endfor %}
</body>

	2.2:forloop对象是for循环中用来判断索引值,是否是第一个或最后一个的对象。
indexPage.html:
<body>
    {% for l in l1 %}
        <p>{{forloop}}</p>
    {% endfor %}
</body>

<body>
    {% for l in l1 %}
        <p>索引值:{{forloop.counter0}}| 索引值(从1开始){{ forloop.counter }}|是否是第一个{{ forloop.first }}|是否是最后一个{{ forloop.last }}</p>
    {% endfor %}
</body>

将if和for循环(forloop)结合起来:
views.py:
def index_func(request):
    l1 = ['max', 'marry', 'jason', 'jack', 'jerry']
    return render(request, 'indexPage.html', locals())

indexPage.html:
<body>
    {% for l in l1 %}
        {% if forloop.first %}
            <p>这是我最后一次循环 {{ l }}</p>
         {% elif forloop.last %}
            <p>这是我最后一次循环 {{ l }}</p>
         {% else %}
            <p>{{ l }}</p>
        {% endif %}
    {% endfor %}
</body>

3.当for循环中出现无法循环的数据类型时,例如空列表,空字符串等,可以在for循环的子代码中添加{% empty %},无法循环的数据类型就会执行{% empty %}的子代码。
views.py:
def index_func(request):
    l1 = ['max', 'marry', 'jason', 'jack', 'jerry']
    l2 = []
    return render(request, 'indexPage.html', locals())

indexPage.html:
<body>
    {% for l in l2 %}
        {{ l }}
        {% empty %}
            <p>无法循环</p>
    {% endfor %}
</body>
# 执行结果:无法循环

4.django模板语法取值操作:只支持句点符,句点符可以点索引值也可以点字典的键。
views.py:
def index_func(request):
    dict1 = {'name':'max','list':[1,2,3,4,5,{'city':'shanghai','province':'guangdonng'}]}
    return render(request, 'indexPage.html', locals())

indexPage.html:
<body>
    {{ dict1.list.5.city }}
</body>
# 执行结果:shanghai

自定义过滤器、标签及inclusion_tag(了解)

"""
如果想要自定义一些模板语法 需要先完成下列的三步走战略
	1.在应用下创建一个名字必须叫templatetags的目录
	2.在上述目录下创建任意名称的py文件	
	3.在上述py文件内先编写两行固定的代码
		from django import template
		register = template.Library()
"""
1.自定义过滤器:最大只支持两个参数,通过 参数1|过滤器名:参数2 来传值
在templatetags中新建一个py文件名叫mytag.py,在mytag中定义过滤器,在views中给html页面传值,传值需要两个大括号包起来,并在前端页面展示:
mytag.py:
@register.filter(name='myplus')
def func1(a, b):  # 函数名随意起,在前段页面只需要根据name就可以找到
    return a + b

views.py:
def index_func(request):
    a = 2
    b = 5
    return render(request, 'indexPage.html', locals())

indexPage.html:
<body>
    {% load mytag %}  # 相当于导入模块
    <p>{{ a|myplus:b }}</p>
</body>
# 执行结果:7

2.自定义标签:参数个数没有限制,在html页面传值通过标签名 空格 值的方式来传值,传值只需要一个大括号。
mytag.py:
@register.simple_tag(name='mytag')
def func2(a,b,c,d):
    return f'{a}:{b}:{c}:{d}'

indexPage.html:
<body>
    {% load mytag %}
    {%  mytag 'max' 'jerry' 'henry' 'kevin' %}
</body>
# 执行结果:max:jerry:henry:kevin

3.自定义inclusion_tag(局部的html代码):先在indexPage.html中调用mymenu,然后将处理好的标签(字符串类型)列表传给menu.html文件,在menu.html中遍历所有的标题。
mytag.py:
@register.inclusion_tag('menu.html',name='mymenu')
def func3(n):
    html = []
    for i in range(n):
        html.append('<li>第%s页<li>' % i)
    return locals()

menu.html:
{% for a in html %}
    {{ a|safe }}  # safe的功能是将字符串的标签显示在页面上
{% endfor %}

indexPage.html:
<body>
    {% load mytag %}
    {% mymenu 20 %}
</body>

模板的继承与导入

模板的继承(重要):多个页面有很多相似的地方,我们可以采取下列方式:
    方式1:传统的复制粘贴
    方式2:模板的继承(相当于划分出一块区域,该区域被模板语法包起来之后就可以自己编辑样式和内容,其他部分依旧沿用被继承的页面)
    
1.在模板中使用block划定可以修改的区域:
{% block 区域名称 %}
{% endblock %}

2.子板继承模板:
{% entends '被继承的html文件' %}
{% block 区域名称 %}
	子板的内容
{% endblock %}
'''form表单中的action如果为空还是向当前页面提交数据'''

PS:模板中至少应该有三个区域,:页面内容区、css样式区、js代码区

3.当我们想要继承模板,并且想要改被继承模板的样式(例如颜色),如果被继承页面没有script标签,我们依旧需要在被继承模板的head标签内划定修改的区域(以修改标题颜色为例):
被继承html文件:
<head>
	{% block css %}
    # 中间可以什么都没有
    {% endblock %}
</head>

继承html文件:
{%  extends 'myhtml.html' %}
{% block css %}
    <style>
        #d1 {  # 把要修改的标题id定义成d1
            color: #5bc0de;
        }
    </style>
{% endblock %}

4.模板也可以继承父类模板之后继续在父类的基础上做扩展,该方法类似面向对象中的继承中的super方法,沿用父类的方法,并且做一些扩展。并且可以连续继承多次。
语法结构:
{% block content %}
{{ block.super }}  (写几次父类中的目标范围的内容就会在子页面出现几次)
# 需要添加的其他内容
{% endblock %}

5.模板的导入:当有一个很漂亮的部分页面,我们想要借鉴的话,可以直接将该模板文件当做模块导入,导入的位置就会插入该内容。
语法结构:{% include 'html文件名' %}

模型层之前期准备

1.自带的sqlite3数据库对时间字段不敏感,有时候会展示错乱,所以我们习惯切换成常见的数据库比如MySQL,django,orm并不会自动帮你创建库 所以需要提前准备好。

2.单独测试某个django文件:
	django默认要求不能单独测试某个文件,需要将整体环境运行起来才可以运行某个py文件。例如我们想测试orm语句中的create,我们需要在views中写一个注册的功能,再在路由层当中匹配对应关系,才可以运行。但是当我们想要运行某个py文件,可以用如下方式:
	测试环境1:pycharm提供的python console(但是终端式无法提供保存功能,只适用于临时编写测试代码)
	测试环境2:在应用中的tests.py或者在应用中自创一个py文件,并且将manage.py中的前四行代码拷贝到tests.py(或自创的py文件内),再自己加入两行代码:
import os
import sys

def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'DjangoProject02.settings')
    import django
    django.setup()
# 以上6行代码写完之后才能导入models模块

ORM常用关键字

"""
models.py:
class User(models.Model):
    name = models.CharField(max_length=32,verbose_name='姓名')
    age = models.IntegerField(verbose_name='年龄')
    register_time = models.DateTimeField(verbose_name='注册时间',auto_now_add=True)  # auto_now_add=True是自动获取数据生成的时间
    def __str__(self):
        return f'对象{self.name}'
"""
1.create():创建数据并直接获取当前创建的数据对象:
res = models.User.objects.create(name='max',age=25)
print(res)  # 对象max
res1 = models.User.objects.create(name='marry',age=15)
print(res1.age)  
'''res拿到的是一个用户对象,该用户对象的命名是按照__str__的返回值来命名,但是该对象也可以通过点的方式拿到用户的信息'''
2.filter():根据相应条件拿到用户对象的列表(query set),如果没有指定条件返回的是所有用户对象的列表:
res2 = models.User.objects.filter()
print(res2)
# <QuerySet [<User: 对象max>, <User: 对象jason>, <User: 对象jerry>, <User: 对象kevin>]
指定筛选条件拿到的还多是一个query set:
res3 = models.User.objects.filter(id=2)
print(res3)  # <QuerySet [<User: 对象max>]>
指定筛选条件可以指定多个值,多个条件之间是and关系:
res4 = models.User.objects.filter(name='max',age=25)
print(res4)  #  <QuerySet [<User: 对象max>]>

6.first(),last():分别指拿到筛选之后的列表中的第一个和最后一个值,和索引取得的结果一样都是一个对象:
res5 = models.User.objects.filter().first()
res6 = models.User.objects.filter().last()
print(res5,res6)  # 对象max 对象marry
"""
两者和索引的区别在于索引取不到值会报错,而上述两者会返回一个None。
"""

7.update():对筛选数据进行更新,如果filter()括号内什么都不写默认修改全部。update修改数据的返回值默认是1,不是数据对象。
models.User.objects.filter(name='marry').update(age=19)

8.delete():删除指定对象,如果filter未指定对象默认删除全部。
models.User.objects.filter(name='marry').delete()
"""
update()和delete()不是数据对象方法,而是query set方法,所以它们不需要取到对象(用first()、last()或索引值拿到对象)才能操作。
"""

9.all():和filter方法一样类似,拿到的都是query set,但是all()默认拿到全部对象的列表,filter拿到指定对象的列表。
res8 = models.User.objects.all()
print(res8)  # <QuerySet [<User: 对象max>, <User: 对象jason>, <User: 对象jerry>, <User: 对象kevin>]>

10.value():拿到指定字段的值。结果是一个quert set,可以看做列表套字典,字典的键是指定字段。value()也是一个query set方法。
res1 = models.User.objects.values('name')
res2 = models.User.objects.all().values('name')
print('res1',res1)  # res1 <QuerySet [{'name': 'max'}, {'name': 'jason'}, {'name': 'jerry'}, {'name': 'kevin'}]>
print('res2',res2)  # res1 <QuerySet [{'name': 'max'}, {'name': 'jason'}, {'name': 'jerry'}, {'name': 'kevin'}]>

11.values_list():拿到指定字段的值,返回的结果是一个queryset,里面的元素是元组,每个元组包含了每条数据指定的字段名对应的数据。
res = models.User.objects.all().values_list('name','age')
print(res)  # <QuerySet [('max', 25), ('jason', 18), ('jerry', 29), ('kevin', 15), ('lili', 17)]>

12.当我们想查看某个对象的sql语句时,可以用如下方法:
	1.用一个queryset对象点query:
res1 = models.User.objects.values('name')
print(res1)  # res1 <QuerySet [{'name': 'max'}, {'name': 'jason'}, {'name': 'jerry'}, {'name': 'kevin'}]>
print(res1.query)  # SELECT `app01_user`.`name` FROM `app01_user`
	2.如果不是queryset对象,我们可以直接在settings加入如下代码,没执行一条操作,都会返回一个sql语句:

models.User.objects.create(name='lili',age=17)  # INSERT INTO `app01_user` (`name`, `age`, `register_time`) VALUES ('lili', 17, '2022-12-14 13:17:11.321862'); args=['lili', 17, '2022-12-14 13:17:11.321862']
"""
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}
"""

12.distinct():跟在queryset后面,去重。去重时会考虑主键。
res = models.User.objects.filter(name='max').distinct()
print(res)  # <QuerySet [<User: 对象max>, <User: 对象max>]>  # 考虑主键两个对象不一样

res = models.User.objects.values_list('name','age').distinct()
print(res)  # <QuerySet [('max', 25), ('jason', 18), ('jerry', 29), ('kevin', 15), ('lili', 17)]>  # 成功去重,因为该queryset不包含主键字段。

13.给queryset通过指定字段排序,默认升序,字段名前面加-是降序。
res = models.User.objects.all().order_by('age')
print(res)  # <QuerySet [<User: 对象kevin>, <User: 对象lili>, <User: 对象jason>, <User: 对象max>, <User: 对象max>, <User: 对象jerry>]>

res = models.User.objects.all().order_by('-age')
print(res)  # <QuerySet [<User: 对象jerry>, <User: 对象max>, <User: 对象max>, <User: 对象jason>, <User: 对象lili>, <User: 对象kevin>]>

14.get():拿到指定的对象,但是如果没有该对象会直接报错。
res = models.User.objects.get(name='jerry')
print(res)  # 对象jerry

15.exclude():拿到条件不满足括号内条件的对象,返回结果是一个queryset。
res = models.User.objects.exclude(name='max')
print(res)  # <QuerySet [<User: 对象jason>, <User: 对象jerry>, <User: 对象kevin>, <User: 对象lili>]>

16.reverse():颠倒顺序(被操作的对象必须是已经排过序的才可以)。
res = models.User.objects.all().order_by('age')
res1 = models.User.objects.all().order_by('age').reverse()
print(res, res1)

17.count():统计结果集中数据的个数。
res = models.User.objects.all().count()
print(res)  # 6

18..exists():判断结果集中是否含有数据,如果有则返回True,没有则返回False。
res = models.User.objects.all().exists()
print(res)  # True
res1 = models.User.objects.filter(pk=100).exists()
print(res1)  # False
"""
1.返回QuerySet对象的方法有:all(),filter(),exclude(),order_by(),reverse(),distinct()
2.返回具体对象的:get(),first(),last()
"""

ORM执行SQL语句

1.用raw(),在括号内编写sql语句:
res = models.User.objects.raw('select * from app01_user')
print(list(res))  # [<User: User object (1)>, <User: User object (2)>]

2.用django.db模块,产生游标对象(封装了pymsql模块):
from django.db import connection
cursor = connection.cursor()
cursor.execute('select * from app01_user')
print(cursor.fetchall())  # ((1, 'max', 25), (2, 'jerry', 18))
"""
fetchone:一次只拿一个;
fetchmany():一次拿指定个数
"""

神奇的双下划线查询

"""
只要还是queryset对象就可以无限制的点queryset对象的方法,除了get(),first(),last(),其余都是queryset方法
"""

1.查询年龄大于18岁的用户:
res = models.User.objects.filter(age__gt=18)
print(res.values_list('name','age'))  # <QuerySet [('max', 25), ('henry', 39), ('harry', 58), ('kevin', 28)]>

2.查询年龄大于等于18岁的用户:
res = models.User.objects.filter(age__gte=18)
print(res.values_list('name','age'))  # <QuerySet [('max', 25), ('jerry', 18), ('jason', 18), ('henry', 39), ('harry', 58), ('kevin', 28)]>   

3.查询年龄小于和小于等于18岁的用户:
res1 = models.User.objects.filter(age__lt=18)  # <QuerySet [<User: User object (6)>]>
res2 = models.User.objects.filter(age__lte=18)  # <QuerySet [<User: User object (2)>, <User: User object (3)>, <User: User object (6)>]>
print(res1,res2)

4.查询年龄是8,18或38的用户对象:
res = models.User.objects.filter(age__in=(8,18,28))
print(res)  # <QuerySet [<User: 对象jerry>, <User: 对象jason>, <User: 对象bob>, <User: 对象kevin>]>  # 从这部开始在models中用__str__将对象名改变了

5.查询年龄范围在18到38岁之间的用户对象(age__range默认首尾包含):
res = models.User.objects.filter(age__range=(18,38))
print(res)  # <QuerySet [<User: 对象max>, <User: 对象jerry>, <User: 对象jason>, <User: 对象kevin>]>

6.查询名字中含有'j'的用户对象(区分大小写): 
res = models.User.objects.filter(name__contains='j')
print(res)  # <QuerySet [<User: 对象jerry>, <User: 对象jason>]>

7.查询名字中含有'J'的用户对象(不区分大小写): 
res = models.User.objects.filter(name__icontains='J')
print(res)  # <QuerySet [<User: 对象jerry>, <User: 对象jason>, <User: 对象Jack>]>

8.查询注册年份是2022的数据:
res = models.User.objects.filter(register_time__year=2022)
print(res)

ORM外键字段的创建

"""
mysql中:
一对多:外键字段在多的一方
一对一:建在任何一方都可以,但是建议建在查询频率较高的一方
"""
1.创建基础表:
	一对多:ORM与MySQL一致,外键字段建在多的一方
	多对多:ORM比MySQL不同,外键字段可以直接建在某张表中(建议放在查询评率较高的表),内部会自动创建第三张表。
	一对一:ORM与MySQL一致,外键字段建在查询频率较高的一方。
        
2.ORM创建:
	针对一对一和一对多,外键括号内要加入级联的参数:on_delete=models.CASCADE。
 	多对多关系表系统会自动创建,并且可以展示在列表中。

models.py:
class Book(models.Model):
    """图书表"""
    title = models.CharField(max_length=32,verbose_name='书名')
    price = models.DecimalField(max_digits=8,decimal_places=2,verbose_name='价格')
    """DecimalField是小数字段类型,max_length是数字总长度,decimal_places是小数点后面位数"""
    publish_time = models.DateField(auto_now_add=True,verbose_name='出版日期')
    publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
'''外键中含有其他表中的主键字段在表中会自动加上id(所以自己不需要写id)'''
# django1.X版本不写db_constraint=models.CASCADE会直接报错
    authors = models.ManyToManyField(to='Auther')
'''多对多不需要添加级联'''

class Publish(models.Model):
    """出版社表"""
    name = models.CharField(max_length=32,verbose_name='名称')
    address = models.CharField(max_length=32,verbose_name='地址')

class Auther(models.Model):
    """作者表"""
    name = models.CharField(max_length=32,verbose_name='姓名')
    age = models.IntegerField(verbose_name='年龄')
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)

class AuthorDetail(models.Model):
    """作者详情表"""
    phone = models.BigIntegerField(verbose_name='手机号')
    address = models.CharField(max_length=64,verbose_name='地址')
"""
在书籍表中我们创建了六个字段,但是字段authors并没有显示在书籍表中的字段名当中。Django默认不讲对多对的外键字段显示在表中,而是会单独创建一个表。针对一对多和一对一,同步到表中之后会自动加id的后缀。
"""

ORM外键字段的创建和操作

除以下内容,其余的内容直接在表上手动添加:
1.针对一对多,可以直接填写表中的实际字段
models.Book.objects.create(title='三国演义',price=888.88,publish_id=1)  # publish_id是外键字段,填写时一定要填已经创建好的

如果一对多中外键字段对应的数据是一个对象,也可以直接添加对象,这种情况下外键字段不用加id(一对多同理):
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(title='红楼梦',price=666.77,publish=publish_obj)

2.针对多对多,django会自己创建一张表,我们对这张表没有办法直接点名字来添加数据,而是需要通过书籍对象点作者外键名的方式:
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.add(1)  # 给pk=3的书籍添加pk=1的作者

一个书籍对象也可以上传多个作者,用逗号隔开(可以多次添加,之前的也会保留):
book_obj = models.Book.objects.filter(pk=4).first()
book_obj.authors.add(1,2)

书籍上传作者也可以上传一个(或多个)作者对象:
author_obj = models.Auther.objects.filter(pk=1).first()
book_obj.authors.add(author_obj)

author_obj3 = models.Auther.objects.filter(pk=3).first()
book_obj6.authors.add(author_obj2,author_obj3)

多对多的修改:当我们想要修改多对多表中的对应关系,我们需要按照以下语法:set()括号内需要写一个元组或者列表
book_obj6 = models.Book.objects.filter(pk=6).first()
book_obj6.authors.set((1,2))

修改时也可以传入对象:
book_obj6 = models.Book.objects.filter(pk=6).first()
author_obj2 = models.Auther.objects.filter(pk=1).first()
author_obj3 = models.Auther.objects.filter(pk=3).first()
book_obj6.authors.set((author_obj2,author_obj3))

多对多删除数据:
book_obj.authors.clear()  # 删除掉多对多关系创建的第三张表:book_authors表中book_id的数据。
"""
对于没有添加数据的表用set修改会报错,应首先用add添加。
"""

删除:我们可以删除指定书对应的作者,可以删除多个,set和remove括号内一次都可以传入多个值
book_obj = models.Book.objects.filter(pk=6).first()
book_obj.authors.remove(1)

ORM跨表查询

"""
复习MySQL跨表查询的思路
1.子查询
	分步操作:将一条SQL语句用括号括起来当做另外一条SQL语句的条件
2.连表操作
		先整合多张表之后基于单表查询即可
			inner join	内连接
			left join	左连接
			right join	右连接
"""
1.正反向查询的概念:
	正向查询:由外键字段所在的表数据查询关联的表数据。比如已知书名查询出版社,外键在书表内,所以是正向查询。
        
2.反向查询的概念:
	反向查询:没有为外键字段的表数据查询关联的表数据。
ps:正反向的核心就看外键字段在不在当前数据所在的表中
ORM跨表查询的口诀(重要):
	正向查询按外键字段
	反向查询按表名小写

基于对象的跨表查询(相当于mysql中的子查询)

正向查询:先拿到已知条件的对象,再通过对象点表中的外键字段名,再点外键对应的表的字段名。如果结果是多个值需要点all(),拿到指定的名字再点values('字段名')。
1.查询主键为1的书籍对应的出版社名称
book_obj = models.Book.objects.filter(pk=1).first()
print(book_obj.publish.name) # 东方出版社

2.查询主键为4的书籍对应的作者姓名:
"""
多对多查询时需要点all(),不然会报错app01.Author.None
"""
book_obj = models.Book.objects.filter(pk=4).first()
print(book_obj.authors.all().values('name'))  # <QuerySet [{'name': 'jerry'}]>

3.查询jason的电话号码
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.author_detail.phone)  # 110

反向查询:先拿到指定数据的对象,再去点该表外键的表名(小写)_set
4.查询北方出版社出版过的书籍
publish_obj = models.Publish.objects.filter(name='北方出版社').first()
print(publish_obj.book_set.all().values('title'))  # <QuerySet [{'title': '老人与海'}, {'title': '红楼梦'}]>

5.查询jason写过的书籍
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.book_set.all().values('title'))  # <QuerySet [{'title': '老人与海'}, {'title': '西游记'}]>

6.查询电话号码是110的作者姓名
auther_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
print(auther_detail_obj.author.name)  # jason

基于双下划线的跨表查询

如果通过已知的条件无法在一张表上面拿到结果,在values()中也是支持跨表查询,直接写表名再用双下划线连接该表明里面的字段名。
1.查询主键为1的书籍对应的出版社名称
res = models.Book.objects.filter(pk=1).values('publish__name')
print(res)  # <QuerySet [{'publish__name': '北方出版社'}]>

2.查询主键为4的书籍对应的作者姓名
res = models.Book.objects.filter(pk=4).values('authors__name')
print(res)  # <QuerySet [{'authors__name': 'jerry'}]>

3.查询jason的电话号码
res = models.Author.objects.filter(name='jason').values('author_detail__phone')
print(res)  # <QuerySet [{'author_detail__phone': 110}]>

4.查询北方出版社出版过的书籍名称和价格
res = models.Publish.objects.filter(name='北方出版社').values('book__title','book__price')
print(res)  # <QuerySet [{'book__title': '老人与海', 'book__price': Decimal('444.44')}, {'book__title': '红楼梦', 'book__price': Decimal('888.88')}]>

5.查询jason写过的书籍名称
res = models.Author.objects.filter(name='jason').values('book__title','book__price')
print(res)  # <QuerySet [{'book__title': '老人与海', 'book__price': Decimal('444.44')}, {'book__title': '西游记', 'book__price': Decimal('444.44')}]>

6.查询电话号码是110的作者姓名
res = models.AuthorDetail.objects.filter(phone=110).values('author__name')
print(res)  # <QuerySet [{'author__name': 'jason'}]>

进阶操作

上述操作都是以已知条件为基础表,而下列方法以目标条件作为基础表,filter中同样支持已知的查询条件
1.查询主键为1的书籍对应的出版社名称层
'''以Publish条件查询,筛选条件是Book,所以筛选条件前要加book__'''
res = models.Publish.objects.filter(book__pk=1).values('name')
print(res)  # <QuerySet [{'name': '北方出版社'}]>

2.查询主键为4的书籍对应的作者姓名
res = models.Author.objects.filter(book__pk=4).values('name')
print(res)  # <QuerySet [{'name': 'jerry'}]>

3.查询jason的电话号码
res = models.AuthorDetail.objects.filter(author__name='jason').values('phone')
print(res)  # <QuerySet [{'phone': 110}]>

4.查询北方出版社出版过的书籍名称和价格
res = models.Book.objects.filter(publish__name='北方出版社').values('title','price')
print(res)  # <QuerySet [{'title': '老人与海', 'price': Decimal('444.44')}, {'title': '红楼梦', 'price': Decimal('888.88')}]>

5.查询jason写过的书籍名称
res = models.Book.objects.filter(authors__name='jason').values('title')
print(res)  # <QuerySet [{'title': '老人与海'}, {'title': '西游记'}]>

6.查询电话号码是110的作者姓名
res = models.Author.objects.filter(author_detail__phone=110).values('name')
print(res)  # <QuerySet [{'name': 'jason'}]>

聚合查询

在ORM中使用聚合函数首先需要导入模块:from django.db.models import Max,Min,Sum,Count,Avg,使用时需要使用aggregate()将聚合函数包起来

不涉及分组查询,在同一个表内根据聚合函数查询数据,如果取了别名在字典中会按照别名显示:
1.统计每一本书的作者个数:
res = models.Book.objects.aggregate(total_num=Count('pk'),max_price=Max('price'),avg_price=Avg('price'))
print(res)  # {'total_num': 5, 'max_price': Decimal('9999.67'), 'avg_price': Decimal('2710.972000')}

分组查询

"""
如果我们执行以下分组代码报错,可以尝试在命令行mysql中(游客模式不行)输入以下代码:set global sql_mode='strict_trans_tables',返回OK则完成修改。
"""
models点什么就是按什么分组,聚合函数中也支持正反向查询的原则:
按照表明分组:models.表名.objects.annotate()
1.统计出每个出版社卖的最便宜的书的价格:
res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
print(res)

2.统计出每个出版社卖的最便宜的书的价格:
es = models.Publish.objects.annotate(cheapest_book=Min('book__price')).values('name','book__title','cheapest_book')
print(res) # 同时拿到了出版社名,书名和书的价格

3.统计不止一个作者的图书:
	1.首先查出所有图书对应的作者个数:
res = models.Book.objects.annotate(count_author=Count('authors__pk')).values('title','count_author')
	2.直接在尾部(values之前)点filter进行筛选:
res = models.Book.objects.annotate(count_author=Count('authors__pk')).filter(count_author__gt=1).values('title','count_author')
print(res)  # <QuerySet [{'title': '三国演义', 'count_author': 3}, {'title': '围城', 'count_author': 2}]>

4.查询每个作者出的书的总价格:
res = models.Author.objects.annotate(sum_price=Sum('book__price'),sum_book=Sum('book__pk')).values('name','sum_book','sum_price')
print(res)  
"""
annotate前面也可以跟filter(),相当于mysql中的where
"""
5.查询每个作者出的书的总价:
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
print(res)

按照字段名分组:
models.表名.objects.values('字段名').annotate()
values()在annotate()之前指按照表中某个字段分组,在annotate()之后指拿到某个字段名。

1.拿到每个publish_id初版的书籍个数:
res = models.Book.objects.values('publish_id').annotate(count_pk=Count('pk')).values('publish_id','count_pk')
print(res)  # <QuerySet [{'publish_id': 1, 'count_pk': 2}, {'publish_id': 2, 'count_pk': 2}, {'publish_id': 3, 'count_pk': 1}]>

F与Q查询

"""
后续添加字段需要指定默认值default=,或者设置允许为空:null=True'''
reper = models.IntegerField(verbose_name='库存数',default=1000)
sale = models.IntegerField(verbose_name='卖出数',null=True)
添加完成之后再执行迁移表的两步操作,新的字段就可以添加到表上
"""
1.当我们需要用同一张表的两个字段名中的数据数据做对比时,我们不能直接用字段名做数据的对比,要是用F('字段名')。
	1.1查询库存数大于卖出数的书籍
res = models.Book.objects.filter(reper__gt=F('sale'))
print(res)

	1.2将所有书的价格涨800
    models.Book.objects.update(price = F('price')+800)
    
	1.3将所有书的名称后面追加爆款
	 from django.db.models.functions import Concat
    from django.db.models import Value
    models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
    
	1.4逗号是and关系:
	查询所有主键是1并且价格大于200的书籍对象:
    res = models.Book.objects.filter(pk=1,price__gt=200)
    print(res)  # <QuerySet [<Book: 书籍对象:三国演义爆款>]>


2.当我们想要修改不同数据之间的条件时,比如(and或or),我们需要使用Q查询
	2.1查询书籍主键是1或者(or)价格大于200块的书籍对象:
    from django.db.models import Q
    res = models.Book.objects.filter(Q(pk=1) | Q(price__gt=200))
    print(res)
   
	2.2查询书籍主键不是1或者价格大于2000的书籍对象
    from django.db.models import Q
    res = models.Book.objects.filter(~Q(pk=1) | Q(price__gt=2000))
    print(res)

标签:总结,name,第十,models,res,filter,objects,print
From: https://www.cnblogs.com/zkz0206/p/16991089.html

相关文章

  • 二分类模型评价指标-总结
    knitr::opts_chunk$set(echo=TRUE)  介绍评价二分类模型的一些指标。1.混淆矩阵预测为正类预测为负类实际为正类TPFN实际为负类FPTN符号标记:TP—将正类预测为正类数......
  • 周末总结12.18
    目录django路由层django请求生命周期流程图(*****)1.路由匹配2.正则匹配3.正则匹配的无名有名分组反向解析基本使用动态路由的反向解析路由分发视图层必会三板斧JsonRespons......
  • 【博学谷学习记录】超强总结,用心分享|接口加解密介绍
    一、介绍在做接口测试的时候,如果是外部用户直接能看到我们的参数,可能会造成接口不安全,比如直接用明文的参数请求接口,把参数自己定义,脏数据就会存到我们的数据库中,严重......
  • 【博学谷学习记录】超强总结,用心分享。数据库的重要知识点。
    一.数据库事务 1.事务特性 原子性:即不可分割性,事务要么全部被执行,要么就全部不被执行。一致性:事务的执行使得数据库从一种正确状态转换成另一种正确状态隔离性:在......
  • android 界面布局 很好的一篇总结 【转】
    布局: ​   在 android 中我们常用的布局方式有这么几种:​1.LinearLayout( 线性布局 ) :(里面只可以有一个控件,并且不能设计这个控件的位置,控件会放到左上角)   ......
  • 第十二周总结
    路由匹配django2.x及以上path第一个参数写什么就匹配什么,django1.x第一个参数是正则表达式转换器(动态匹配)str:匹配除路径分隔符外的任何非空字符串(常用)int:匹配0或者任......
  • 队列总结_legend
    队列总结:Queue (1)队列的基本知识:        (1.1)特点:尾进头出,先进先出。(rearinfrontout)         (1.2)与线性表比较:        队列......
  • django总结
    内容导航django路由层内容详细django路由层1.路由匹配django2.X及以上path第一个参数些什么就匹配什么django1.X第一个参数是正则表达式无论匹配什么版本dja......
  • Nginx入门--学习总结
    Nginx入门核心功能:反向代理、负载均衡、动静分离nginx的安装、启动nginx常用命令,进入/usr/local/nginx/sbin./nginx--启动./nginx-sstop--停止nginx./ng......
  • 十二周总结
    十二周总结django请求生命周期流程图Django路由层1.路由匹配django1.X版本路由的第一个参数是正则表达式django2.X及以上版本的则是path写什么就匹配什么......