django框架【待续】

简介

MVC与MTV模型

MVC

MVC就是把Web应用分为模型(M)控制器©和视图(V)三层他们之间以一种插件式的、松耦合的方式连接在一起模型负责业务对象与数据库的映射(ORM)视图负责与用户的交互(页面)控制器接受用户的输入调用模型和视图完成用户的请求。

MTV

Django的MTV模式本质上和MVC是一样的也是为了各组件间保持松耦合关系只是定义上有些许不同Django的MTV分别是值

  1. M 代表模型Model 负责业务对象和数据库的关系映射(ORM)。
  2. T 代表模板 (Template)负责如何把页面展示给用户(html)。
  3. V 代表视图View 负责业务逻辑并在适当时候调用Model和Template。

除了以上三层之外还需要一个URL分发器它的作用是将一个个URL的页面请求分发给不同的View处理View再调用相应的Model和Template。

创建项目

# 在命令行执行以下指令会在当前目录生成一个名为appserver的文件夹该文件夹中包含Django框架的一系列基础文件
django-admin startproject appserver

# 切换到appserver目录下边
cd mysite 
# 创建功能模块user此处的startapp代表创建application下的一个功能模块。
python manage.py startapp user

# 运行http://127.0.0.1:8000
python manage.py runserver 8000

目录

-manage.py---项目入口,执行一些命令
-项目名
    -settings.py  全局配置信息
    -urls.py      总路由
-app名字
    -migrations   数据库迁移的记录
    -models.py    数据库表模型
    -views.py     视图函数

生命周期

1、路由层根据不同的地址执行不同的视图函数详见urls.py

2、视图层定义处理业务逻辑的视图函数详见views.py

3、模型层 跟数据库打交道的详解models.py

4、模板层待返回给浏览器的html文件详见templates
在这里插入图片描述

静态文件配置无用

对于前后端分离的项目静态文件一般用nginx代理不用特意在webServer框架再配置

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]

前端对应配置

# 加载静态目录
{% load static %}
<script src="{% static 'js/jQuery3.6.0.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
<script src="{% static 'js/sweetalert.min.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
<link rel="shortcut icon" href="{% static "img/favicon.ico" %}">

模板语法
{{}}用于取值{% %}用于逻辑

启动django启动

  1. manage.py是入口执行了execute_from_command_line(sys.argv)

  2. 进入management_init_.py执行了

    cmd : python manage.py runserver 8000
    argv: runserver 8000
    
    def execute_from_command_line(argv=None):
        """Run a ManagementUtility."""
        utility = ManagementUtility(argv)
        utility.execute()
    
  3. execute()方法中校验参数正误并且执行self.fetch_command("runserver").run_from_argv(self.argv)

  4. fetch_command(“runserver”)找到了对应的runserver模块的命令
    在这里插入图片描述

  5. 执行CommandObj.run_from_argv(self.argv)再跳到self.execute(*args, **cmd_options)再获取到output = self.handle(*args, **options)这个handler是runserver.handle()

  6. 检查ip等信息后执行self.run(**options)最终执行了好几个类的run方法后到了最后一个run

def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
    server_address = (addr, port)
    if threading:
        httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
    else:
        httpd_cls = server_cls
        
    # 它是WSGI服务器与django之间相互通信的唯一枢纽通道当WSGI服务对象收到socket请求后会将这个请求传递给django
    httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
    if threading:
        # ThreadingMixIn.daemon_threads indicates how threads will behave on an
        # abrupt shutdown; like quitting the server by the user or restarting
        # by the auto-reloader. True means the server will not wait for thread
        # termination before it quits. This will make auto-reloader faster
        # and will prevent the need to kill the server manually if a thread
        # isn't terminating correctly.
        httpd.daemon_threads = True
    httpd.set_app(wsgi_handler)
    # 启动非堵塞网络监听服务
    httpd.serve_forever()

路由

# urls.py 
from django.conf.urls import url

urlpatterns = [
     url(regex, view, kwargs=None, name=None), 
     # 一个注册接口
     url(r'^register/', account.register, name='register'),
]
#函数url关键参数介绍
# regex正则表达式,用来匹配url地址的路径部分
# view通常为一个视图函数用来处理业务逻辑
# kwargs有名分组
# name反向解析
  1. django的路由匹配是自上而下的迭代匹配到一个规则就不再向下而gin框架则是利用前缀树时间复杂度更小。

  2. 因此django的首页配置要放到最后。

    	...
        # 充值页面
        url('^magic/', order.pay, name='pay'),
        url('^order/', order.order, name='order'),
        url('^result/', order.pay_result, name='result'),
    
        url(r'^', home.index, name='index'),
    ]
    
  3. 路由以/结尾不输入/也能匹配到的原因在配置文件settings.py中有一个参数APPEND_SLASH该参数有两个值True或False当APPEND_SLASH=True如果配置文件中没有该配置APPEND_SLASH的默认值为True并且用户请求的url地址的路径部分不是以 / 结尾Django会拿着路径部分即index去路由表中匹配正则表达式发现匹配不成功那么Django会在路径后加 /再去路由表中匹配如果匹配失败则会返回路径未找到如果匹配成功则会返回重定向信息给浏览器。django的全局配置在from django.conf import settings

分组

适用于http://127.0.0.1:8000/article/id/id为某些记录的主键

我还是喜欢用http://127.0.0.1:8000/article?id=id这种restfulapi的形式QAQ

注意分组是正则表达式的功能。

无名分组

# 路由
url(r'^aritcle/(\d+)/$',views.article),

# 视图函数需要配套接收匹配成功会调用article(request,id)
def article(request,article_id):
    return HttpResponse('id为 %s 的文章内容...' %article_id)

有名分组

url(r'^aritcle/(?P<article_id>\d+)/$',views.article)

# 视图函数需要配套接收,匹配成功会调用article(request,article_id=id)
def article(request,article_id):
    return HttpResponse('id为 %s 的文章内容...' %article_id)

路由分发

  1. 每个功能模块应该有自己的urls.py文件。
  2. urls过多会导致路由匹配速度下降。
from django.conf.urls import url,include
from django.contrib import admin
# 总路由表
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    
    # 新增两条路由注意不能以$结尾。
    # include函数就是做分发操作的。
    # 总路由里面写前缀即可。
    # app01.urls是因为app01已经在settings中注册了否则无法获取app01/urls.py
    url(r'^app01/', include('app01.urls')),
    url(r'^app02/', include('app02.urls')),
]from app01 import urls as app01_urls
from app02 import urls as app02_urls
url(r'^app01/', include(app01_urls)),
url(r'^app02/', include(app02_urls)),

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'user',  # apps目录已经被加到环境变量了直接能找到user
    'home',
    'course',
    'order',
]
urlpatterns = [
    path('xadmin/', xadmin.site.urls),
    re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
    path('home/', include('home.urls')),
    path('user/', include('user.urls')),
    path('course/', include('course.urls')),
    path('order/', include('order.urls'))
]

反向解析

url(r'^register/', account.register, name='register'),

编写一条url(regex, view, kwargs=None, name=None)时可以通过参数name为url地址的路径部分起一个别名项目中就可以通过别名来获取这个路径

from django.shortcuts import render 
from django.shortcuts import reverse # 用于反向解析
from django.shortcuts import redirect #用于重定向页面
from django.shortcuts import HttpResponse

def login(request):
    if request.method == 'GET':
        # 当为get请求时返回login.html页面,页面中的{% url 'login_page' %}会被反向解析成路径/login/
        return render(request, 'login.html')
        
    url = reverse('index_page')  # reverse会将别名'index_page'反向解析成路径/index/       
    return redirect(url) # 重定向到/index/

# 前端
<a href="{% url 'index' %}"><img src="{% static 'img/head.png' %}" alt="" style="width: 100px"></a>

反向解析结合分组

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    
    url(r'^aritcle/(\d+)/$',views.article,name='article_page'), # 无名分组
    url(r'^user/(?P<uid>\d+)/$',views.article,name='user_page'), # 有名分组
]
# 1 针对无名分组
在views.py中反向解析的使用
url = reverse('article_page',args=(1,)) 
在模版login.html文件中反向解析的使用
{% url 'article_page' 1 %}

    
# 2 针对有名分组
在views.py中反向解析的使用
url = reverse('user_page',kwargs={'uid':1}) 
在模版login.html文件中反向解析的使用
{% url 'user_page' uid=1 %}

名称空间

如果别名存在重复那么在反向解析时则会出现覆盖。推荐给路由起名字时按name=“模块_功能_view”一般不会重复

# 总路由表
urlpatterns = [
    url(r'^admin/', admin.site.urls),
	
    # 传给include功能一个元组元组的第一个值是路由分发的地址第二个值则是名称空间起的名字
    url(r'^app01/', include(('app01.urls','app01'))),
    url(r'^app02/', include(('app02.urls','app02'))),
]
# 视图
from django.shortcuts import render
from django.shortcuts import HttpResponse
from django.shortcuts import reverse

def index(request):
    url=reverse('app01:index_page') # 解析的是名称空间app01下的别名'index_page'
    return HttpResponse('app01的index页面反向解析结果为%s' %url)

1、在视图函数中基于名称空间的反向解析用法如下
url=reverse('名称空间的名字:待解析的别名')

2、在模版里基于名称空间的反向解析用法如下
<a href="{% url '名称空间的名字:待解析的别名'%}">哈哈</a>

re_path与path

  1. Django2.0中的re_path与django1.0的url一样。
  2. 在Django2.0中新增了一个path功能用来解决数据类型转换问题与正则表达式冗余问题
urlpatterns = [
    # 问题一数据类型转换
    # 正则表达式会将请求路径中的年份匹配成功然后以str类型传递函数year_archive在函数year_archive中如果想以int类型的格式处理年份则必须进行数据类型转换
    re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),

    # 问题二正则表达式冗余
    # 下述三个路由中匹配article_id采用了同样的正则表达式重复编写了三遍存在冗余问题并且极不容易管理因为一旦article_id规则需要改变则必须同时修改三处代码
    re_path(r'^article/(?P<article_id>[a-zA-Z0-9]+)/detail/$', views.detail_view),
    re_path(r'^articles/(?P<article_id>[a-zA-Z0-9]+)/edit/$', views.edit_view),
    re_path(r'^articles/(?P<article_id>[a-zA-Z0-9]+)/delete/$', views.delete_view),
]


---------------------------------------------------------

urlpatterns = [
    # 问题一的解决方案
    path('articles/<int:year>/', views.year_archive), # <int:year>相当于一个有名分组其中int是django提供的转换器相当于正则表达式专门用于匹配数字类型而year则是我们为有名分组命的名并且int会将匹配成功的结果转换成整型后按照格式year=整型值传给函数year_archive


    # 问题二解决方法用一个int转换器可以替代多处正则表达式
    path('articles/<int:article_id>/detail/', views.detail_view), 
    path('articles/<int:article_id>/edit/', views.edit_view),
    path('articles/<int:article_id>/delete/', views.delete_view),
]

#1、path与re_path或者1.0中的url的不同之处是传给path的第一个参数不再是正则表达式而是一个完全匹配的路径相同之处是第一个参数中的匹配字符均无需加前导斜杠

#2、使用尖括号(<>)从url中捕获值相当于有名分组

#3、<>中可以包含一个转化器类型converter type比如使用 <int:name> 使用了转换器int。若果没有转化器将匹配任何字符串当然也包括了 / 字符

默认支持五种转换器
1. str,匹配除了路径分隔符/之外的非空字符串这是默认的形式
2. int,匹配正整数包含03. slug,匹配字母、数字以及横杠、下划线组成的字符串。
4. uuid,匹配格式化的uuid如 075194d3-6885-417e-a8a8-6c931e272f00。
5. path,匹配任何非空字符串包含了路径分隔符/

自定义转换器

模板如下

class MonthConverter:
	# 正则
	# 属性名必须为regex
    regex='\d{2}'
	
	# to_python用于将参数类型转换后传递到视图函数
    def to_python(self, value):
        return int(value)
	
	# to_url用于url反向引用
    def to_url(self, value):
    	# # 匹配的regex是两个数字返回的结果也必须是两个数字
        return value 

视图

视图函数简称视图属于Django的视图层默认定义在views.py文件中是用来处理web请求信息以及返回响应信息的函数所以研究视图函数只需熟练掌握两个对象即可请求对象(HttpRequest)和响应对象(HttpResponse)

HttpRequest

常用方法

个人喜欢Form(data=request.POST)直接导入form方便校验

.HttpRequest.method
获取请求使用的方法值为纯大写的字符串格式。例如"GET""POST".HttpRequest.GET
值为一个类似于字典的QueryDict对象封装了GET请求的所有参数可通过HttpRequest.GET.get('键')获取相对应的值
  
三.HttpRequest.POST
值为一个类似于字典的QueryDict对象封装了POST请求所包含的表单数据可通过HttpRequest.POST.get('键')获取相对应的值
   
针对表单中checkbox类型的input标签、select标签提交的数据键对应的值为多个需要用HttpRequest.POST.getlist("hobbies")获取存有多个值的列表,同理也有HttpRequest.GET.getlist("键").HttpRequest.body
ajax可以提交的数据格式有1、编码格式1 2、编码格式2 3、json当ajax采用POST方法提交前两种格式的数据时django的处理方案同上但是当ajax采用POST方法提交json格式的数据时django会将接收到的数据存放于HttpRequest.body此时需要我们自己对HttpRequest.body属性值做反序列化操作。

五.HttpRequest.FILES
HttpRequest.FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才会包含数据。否则FILES 将为一个空的类似于字典的对象。该属性值为一个类似于字典的对象可以包含多组key:value对应多个上传的文件class MultiValueDict(dict). [filename1:[obj1,obj2]]
上传文件
with open(filePath,"wb") as writer:
	for line in request.FILES.get("filename"):
		writer.write(line).HttpRequest.path
获取url地址的路径部分只包含路径部分

七.HttpRequest.get_full_path()
获取url地址的完整path既包含路径又包含参数部分

http://127.0.0.1:8001/order/?name=egon&age=10
HttpRequest.path的值为"/order/"
HttpRequest.get_full_path()的值为"/order/?name=egon&age=10".HttpRequest.META
值为包含了HTTP协议的请求头数据的Python字典字典中的key及期对应值的解释如下
CONTENT_LENGTH —— 请求的正文的长度是一个字符串。
CONTENT_TYPE —— 请求的正文的MIME类型。
HTTP_ACCEPT —— 响应可接收的Content-Type。
HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
HTTP_HOST —— 客服端发送数据的目标主机与端口
HTTP_REFERER —— Referring 页面。
HTTP_USER_AGENT —— 客户端使用的软件版本信息
QUERY_STRING —— 单个字符串形式的查询字符串未解析过的形式。
REMOTE_ADDR —— 客户端的IP地址。
REMOTE_HOST —— 客户端的主机名。
REMOTE_USER —— 服务器认证后的用户。
REQUEST_METHOD —— 一个字符串例如"GET""POST"。
SERVER_NAME —— 服务器的主机名。
SERVER_PORT —— 服务器的端口是一个字符串。
从上面可以看到除 CONTENT_LENGTH 和 CONTENT_TYPE 之外HTTP协议的请求头数据转换为 META 的键时
都会
1、将所有字母大写
2、将单词的连接符替换为下划线
3、加上前缀HTTP_。
所以一个叫做 X-Bender 的头部将转换成 META 中的 HTTP_X_BENDER 键。
   
九.HttpRequest.COOKIES
一个标准的Python字典包含所有的cookie。键和值都为字符串。

十.HttpRequest.session
一个既可读又可写的类似于字典的对象表示当前的会话。只有当Django 启用会话的支持时才可用。

十一.HttpRequest.user(用户认证组件下使用)
一个 AUTH_USER_MODEL 类型的对象表示当前登录的用户也可以自定义为某User对象。

十二.HttpRequest.is_ajax()
如果请求是通过XMLHttpRequest 发起的则返回True方法是检查 HTTP_X_REQUESTED_WITH 相应的首部是否是字符串'XMLHttpRequest'

HttpResponse

响应可以是任何形式的内容比如一个HTML文件的内容一个重定向一个404错误一个XML文档或者一张图片等。

在这里插入图片描述
快捷方式
在这里插入图片描述

# 前后端不分离
from django.shortcuts import HttpResponse,render,redirect
# json
from django.http import JsonResponse

# 实际上返回文件也不应该用最基本的HttpResonse()
# def __init__(self, content=b'', *args, **kwargs):
# 参数传入centent就行
return HttpResponse(png)

return render(request, 'register.html', locals(), status=200)
def render(request, template_name, context=None, content_type=None, status=None, using=None):
	"""
	1. request:request对象
	2. template_name:模板名字(利用配置文件里的templates去反射文件)
	3. context: 本次请求上下文数据,可以传入locals()自动将数据导入
	4. content_type: http协议的Content-Type
	5. status: http协议的状态码
	6. using: ...
	"""
    content = loader.render_to_string(template_name, context, request, using=using)
    return HttpResponse(content, content_type, status)

return redirect('/some/url/')
def redirect(to, *args, permanent=False, **kwargs):
    """
    1. to: 目标url
    2. permanent: 是否永久决定状态码是301还是302
	301表示旧地址A的资源已经被永久地移除了即这个资源不可访问了。搜索引擎在抓取新内容的同时也将旧的网址转换为重定向之后的地址
  302表示旧地址A的资源还在即这个资源仍然可以访问这个重定向只是临时地从旧地址A跳转到地址B搜索引擎会抓取新的内容、并且会保存旧的网址。 从SEO层面考虑302要好于301.
    """
    redirect_class = HttpResponsePermanentRedirect if permanent else HttpResponseRedirect
    return redirect_class(resolve_url(to, *args, **kwargs))

Json

from django.http import JsonResponse

def my_view(request):
    data=['fuck','off']
    return JsonResponse(data,safe=False)
    # 默认safe=True代表只能序列化字典对象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.'
            )
        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_params可以传入一些指定参数如防止将中文字符串转为asciiensure_ascii:False
传入的json_dumps_params会被django内置的json序列化方法捕获到使用。

FBV和CBV

django的视图层由两种形式构成FBV和CBV

1、FBV基于函数的视图Function base view我们之前一直介绍的都是FBV

2、CBV基于类的视图(Class base view)

其实CBV有FBV并无太大差异视图类在经过as_view()和dispatch()后就变成了视图函数。

from django.urls import path,re_path
from app01 import views

urlpatterns = [
	# 必须调用类下的方法as_view
    re_path(r'^login/',views.LoginView.as_view()),
]

from django.shortcuts import render,HttpResponse,redirect
from django.views import View

class LoginView(View):

	# 菜鸟如我一般不用重写
    def dispatch(self, request, *args, **kwargs): 
    	# 可在该方法内做一些预处理操作
    	# 必须继承父类的dispatch功能
        # 当请求url为http://127.0.0.1:8008/login/会先触发dispatch的执行
        # 如果http协议的请求方法为GET则调用下述get方法
        # 如果http协议的请求方法为POST则调用下述post方法
        return super().dispatch(request, *args, **kwargs)
	
	# 会被反射request.method取到
    def get(self,request):
        return render(request,'login.html')
	
	# # 会被反射request.method取到
    def post(self,request):
        return HttpResponse(res)

原生的as_view()和dispatch()方法比较简单

def dispatch(self, request, *args, **kwargs):
    if request.method.lower() in ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']:	
    	# 反射获取视图函数
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    else:
    	# 默认method_not_allowed 403 视图函数
        handler = self.http_method_not_allowed
    return handler(request, *args, **kwargs)

@classonlymethod
def as_view(cls, **initkwargs):

	# 略过
    for key in initkwargs:
        if key in cls.http_method_names:
            raise TypeError("You tried to pass in the %s method name as a "
                            "keyword argument to %s(). Don't do that."
                            % (key, cls.__name__))
        if not hasattr(cls, key):
            raise TypeError("%s() received an invalid keyword %r. as_view "
                            "only accepts arguments that are already "
                            "attributes of the class." % (cls.__name__, key))
	
	# 核心
    def view(request, *args, **kwargs):
    	# self = 视图类()
        self = cls(**initkwargs)
        if hasattr(self, 'get') and not hasattr(self, 'head'):
            self.head = self.get
            
        # 赋值request和参数可以通过self.request获取request对象了
        self.setup(request, *args, **kwargs)
        # 刚赋值怎么可能取不到呢除非你手贱重写了setup()
        if not hasattr(self, 'request'):
       		# 询问你是否手贱重写了setup()
            raise AttributeError(
                "%s instance has no 'request' attribute. Did you override "
                "setup() and forget to call super()?" % cls.__name__
            )
        # 调用dispatch()    
        return self.dispatch(request, *args, **kwargs)
    
    # 给view赋值伪装
    view.view_class = cls
    view.view_initkwargs = initkwargs

    # take name and docstring from class
    update_wrapper(view, cls, updated=())

    # and possible attributes set by decorators
    # like csrf_exempt from dispatch
    update_wrapper(view, cls.dispatch, assigned=())
	
	# 返回装饰器后的view
    return view

模板前后端分离不用看

django的模板=HTML代码+模板语法

一、变量{{ 变量名 }}
	1.1 深度查询句点符的应用
    1.2 过滤器
二、标签{% 标签名 %}
三、自定义标签和过滤器
四、模板的导入和继承

变量

return render(request,'test.html',{'msg':msg,'dic':dic,'obj':obj,'li':li})#locals()会将函数内定义的名字与值转换为字典中的k与v
return render(request,'test.html',locals()) 
<p>{{ msg }}</p>
<p>{{ dic }}</p>
<p>{{ obj }}</p>
<p>{{ li }}</p>

<!--调用字符串对象的upper方法注意不要加括号-->
<p>{{ msg.upper }}</p>
<!--取字典中k1对应的值-->
<p>{{ dic.k1 }}</p>
<!--取对象的name属性-->
<p>{{ obj.name }}</p>
<!--取列表的第2个元素,然后变成大写-->
<p>{{ li.1.upper }}</p>
<!--取列表的第3个元素并取该元素的age属性-->
<p>{{ li.2.age }}</p>

过滤器

可以理解为Linux的管道符将前面的结果传给后面运算

{{ 变量名|过滤器名:传给过滤器的参数 }}

#1、default
#作用如果一个变量值是False或者为空使用default后指定的默认值否则使用变量本身的值
{{ value|default:"nothing" }}

#2、length
#作用返回值的长度。它对字符串、列表、字典等容器类型都起作用
{{ value|length }}

#3、filesizeformat
#作用将值的格式化为一个"人类可读的"文件尺寸(如13KB、4.1 MB、102bytes等等
{{ value|filesizeformat }}

#4、date
#作用将日期按照指定的格式输出如果value=datetime.datetime.now(),按照格式Y-m-d则输出2019-02-02
{{ value|date:"Y-m-d" }}  

#5、slice
#作用对输出的字符串进行切片操作顾头不顾尾
{{ value|slice:"0:2" }} 

#6、truncatechars
#作用如果字符串字符多于指定的字符数量那么会被截断。截断的字符串将以可翻译的省略号序列“...”结尾注意8个字符也包含末尾的3个点
{{ value|truncatechars:8 }}

#7、truncatewords
#作用同truncatechars但truncatewords是按照单词截断注意末尾的3个点不算作单词
{{ value|truncatewords:2 }}

#8、safe
#作用出于安全考虑Django的模板会对HTML标签、JS等语法标签进行自动转义,例如value="<script>alert(123)</script>"模板变量{{ value }}会被渲染成&lt;script&gt;alert(123)&lt;/script&gt;交给浏览器后会被解析成普通字符”<script>alert(123)</script>“失去了js代码的语法意义但如果我们就想让模板变量{{ value }}被渲染的结果有语法意义那么就用到了过滤器safe比如value='<a href="https://www.baidu.com">点我啊</a>'在被safe过滤器处理后就成为了真正的超链接不加safe过滤器则会当做普通字符显示’<a href="https://www.baidu.com">点我啊</a>‘
{{ value|safe }}
过滤器描述示例
upper以大写方式输出{{ user.name | upper }}
add给value加上一个数值{{ user.age | add:”5” }}
addslashes单引号加上转义号{{ val | addslashes }}
capfirst第一个字母大写{{ ‘good’| capfirst }} 返回”Good”
center输出指定长度的字符串把变量居中{{ “abcd”| center:”50” }}
cut删除指定字符串{{ “You are not a Englishman” | cut:”not” }}
default_if_none如果值为None, 则使用默认值代替{{ val | default_if_none:"default" }}
dictsort按某字段排序变量必须是一个dictionary{% for moment in moments | dictsort:”id” %}
dictsortreversed按某字段倒序排序变量必须是dictionary{% for moment in moments | dictsortreversed:”id” %}
divisibleby判断是否可以被数字整除{{ 224 | divisibleby:10}}
escape按HTML转义比如将”<”转换为”&lt”{{ html | escape }}
first返回列表的第1个元素变量必须是一个列表{{ list | first }}
floatformat转换为指定精度的小数默认保留1位小数{{ 3.1415926 | floatformat:3 }} 返回 3.142 四舍五入
get_digit从个位数开始截取指定位置的数字{{ 123456 | get_digit:’1’}}
join用指定分隔符连接列表{{ [‘abc’,’45’] | join:’’ }} 返回 abc45
length_is检查列表字符串长度是否符合指定的值{{ ‘hello’| length_is:’3’ }}
linebreaks用p标签或br标签包裹变量{{ “Hi\n\nDavid”|linebreaks }} 返回

Hi

David

linebreaksbr用br标签代替换行符{{ hi\r\n\r\n | linebreaksbr }}
linenumbers为变量中的每一行加上行号{{ val | linenumbers }}
ljust输出指定长度的字符串变量左对齐{{‘ab’|ljust:5}}返回 ‘ab ’
lower字符串变小写{{ "AB" | lower }}
make_list将字符串转换为列表{{ "150" | make_list }}
random返回列表的随机一项{{ list | random }}
removetags删除字符串中指定的HTML标记{{value | removetags: “h1 h2”}}
rjust输出指定长度的字符串变量右对齐{{‘ab’|rjust:5}}返回 ‘ab ’
slugify在字符串中留下减号和下划线其它符号删除空格用减号替换{{ '5-2=3and5 2=3' }}
time返回日期的时间部分{{ time_str | time }}
truncatewords_html保留其中的HTML标签{{ 'p This is a pen p' | truncatewords_html: 5 }}
urlencode将字符串中的特殊字符转换为url兼容表达方式{{ ‘http://www.aaa.com/foo?a=b&b=c’ | urlencode}}
urlize将变量字符串中的url由纯文本变为链接{{ url | urlize }}
yesno将布尔变量转换为字符串yes, no 或maybe{{ True | yesno }}
## 标签 标签是为了在模板中完成一些特殊功能,语法为{% 标签名 %}一些标签还需要搭配结束标签 {% endtag %}

循环

#1、遍历每一个元素
{% for person in person_list %}
    <p>{{ person.name }}</p>
{% endfor %}

#2、可以利用{% for obj in list reversed %}反向循环。

#3、遍历一个字典
{% for key,val in dic.items %}
    <p>{{ key }}:{{ val }}</p>
{% endfor %}

#4、循环序号可以通过{{ forloop }}显示 
{% for name in names %}
    <p>{{ forloop.counter0 }} {{ name }}</p>
{% endfor %}
forloop.counter            当前循环的索引值从1开始
forloop.counter0           当前循环的索引值从0开始
forloop.revcounter         当前循环的倒序索引值从1开始
forloop.revcounter0        当前循环的倒序索引值从0开始
forloop.first              当前循环是第一次循环则返回True否则返回False
forloop.last               当前循环是最后一次循环则返回True否则返回False
forloop.parentloop         本层循环的外层循环

#5、for标签可以带有一个可选的{% empty %} 从句在变量person_list为空或者没有被找到时则执行empty子句
{% for person in person_list %}
    <p>{{ person.name }}</p>
{% empty %}
    <p>sorry,no person here</p>
{% endfor %}

分支

# 1、注意
{% if 条件 %}条件为真时if的子句才会生效条件也可以是一个变量if会对变量进行求值在变量值为空、或者视图没有为其传值的情况下均为False

# 2、具体语法
{% if num > 100 or num < 0 %}
    <p>无效</p>
{% elif num > 80 and num < 100 %}
    <p>优秀</p>
{% else %}
    <p>凑活吧</p>
{% endif %}

#3、if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。

csrf

前后端不分离需要在表单加入{% csrf_token %}

<form>
{% csrf_token %}
</form>

# 1、在GET请求到form表单时标签{% csrf_token%}会被渲染成一个隐藏的input标签该标签包含了由服务端生成的一串随机字符串,如<input type="hidden" name="csrfmiddlewaretoken" value="dmje28mFo...OvnZ5">
# 2、在使用form表单提交POST请求时会提交上述随机字符串服务端在接收到该POST请求时会对比该随机字符串对比成功则处理该POST请求否则拒绝以此来确定客户端的身份

with

# with标签用来为一个复杂的变量名起别名,如果变量的值来自于数据库在起别名后只需要使用别名即可无需每次都向数据库发送请求来重新获取变量的值
{% with li.1.upper as v %}
    {{ v }}
{% endwith %}

自定义过滤器和标签

  1. 将app注册到settings.py
  2. 在文件夹app中创建子文件夹templatetags(文件夹名只能是templatetags)
    在这里插入图片描述
  3. 创建Py 文件自定义过滤器和标签
from django import template
# 注意变量名必须为register,不可改变
register = template.Library() 

#1、自定义过滤器
@register.filter
# 自定义的过滤器只能定义最多两个参数针对{{ value1 | filter_multi:value2 }}
# 参数传递为v1=value1,v2=value2
def my_multi_filter(v1 ,v2): 
    return  v1 * v2

#2、自定义标签
@register.simple_tag
# # 自定义的标签可以定义多个参数
def my_multi_tag(v1, v2): 
    return v1 * v2

#3、自定义标签扩展之mark_safe
# 注释用内置的标签safe来让标签内容有语法意义如果想让自定义标签处理的结果也有语法意义则不能使用内置标签safe了需要使用mark_safe可以实现与内置标签safe同样的功能
from django.utils.safestring import mark_safe
@register.simple_tag
def my_input_tag(id, name):
    res = "<input type='text' id='%s' name='%s' />" % (id, name)
    return mark_safe(res)
  1. 使用
<!--必须先加载存有自定义过滤器和标签的文件-->
{% load inclusion_tags %}

# 过滤器
<!--salary的值为10经过滤器my_multi_filter的处理结果为120-->
{{ salary|my_multi_filter:12 }}

# 标签
<!--结果为2-->
{% my_multi_tag 1 2 %}

# 标签
结果为一个input标签该表的属性id="inp1" name="username"
注意input的属性值均为字符串类型所以my_input_tag后的两个值均为字符串类型
{% my_input_tag "inp1" "username" %} 



# 对比
#1、自定义过滤器只能传两个参数而自定义标签却可以传多个参数
#2、过滤器可以用于if判断而标签不能
{% if salary|my_multi_filter:12 > 200 %}
    <p>优秀</p>
{% else %}
    <p>垃圾</p>
{% endif %}

在这里插入图片描述

模板的导入和继承

模板文件彼此之间可能会有大量冗余代码为此django提供了专门的语法来解决这个问题,主要围绕三种标签的使用include标签、extends标签、block标签。

在这里插入图片描述

#作用在一个模板文件中引入/重用另外一个模板文件的内容
{% include '模版名称' %}

<div class="row">
    <div class="col-md-3">
        <!--在base.html引入advertise.html文件的内容-->
        {% include "advertise.html" %}
    </div>
    <div class="col-md-9"></div>
</div>

模板的继承\派生之extends标签、block标签

#作用在一个模板文件中引入/重用另外一个模板文件的内容
# include有的功能extends全都有
# extends可以搭配一个block标签用于在继承的基础上定制新的内容
{% extends "模版名称" %}

# 定义base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{% block tilte %}{% endblock %}</title>
    {% load static %}
    <script src="{% static 'js/jQuery3.6.0.js' %}"></script>
    <script src="{% static 'js/bootstrap.min.js' %}"></script>
    <script src="{% static 'js/sweetalert.min.js' %}"></script>
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="shortcut icon" href="{% static "img/favicon.ico" %}" />
    <style>
        .navbar-default{
            border-radius: 0;
        }
        .account{
            width: 400px;
            margin-top: 30px;
            margin-left: auto;
            margin-right: auto;
            border: 1px solid #f0f0f0;
            padding: 10px 30px 30px 30px;
            -webkit-box-shadow: 5px 10px 10px rgba(0,0,0,.05);
            box-shadow: 5px 10px 10px rgba(0,0,0,.05);
        }
        .account .title{
            font-size: 25px;
            font-weight: bold;
            text-align: center;
        }
        .account .form-group{
            margin-bottom: 20px;
        }
    </style>
    {% block css %}{% endblock %}
</head>
<body>
    <nav class="navbar navbar-default">
      <div class="container">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#" style="padding-top: 0">
              <a href="{% url 'index' %}"><img src="{% static 'img/head.png' %}" alt="" style="width: 100px"></a>
          </a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
          <ul class="nav navbar-nav">
            <li><a href="#">产品功能</a></li>
            <li><a href="#">企业方案</a></li>
              <li><a href="#">帮助文档</a></li>
              <li><a href="{% url 'pay' %}">价格</a></li>
          </ul>

          <ul class="nav navbar-nav navbar-right">
              {% if request.authentication %}
                <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ request.authentication.username }} <span class="caret"></span></a>
              <ul class="dropdown-menu">
                <li><a href="{% url 'project_list' %}">管理中心</a></li>
                <li role="separator" class="divider"></li>
                <li><a href="{% url 'logout' %}">退 出</a></li>
              </ul>
            </li>
              {% else %}
                  <li><a href="{% url 'smslogin' %}">登 录</a></li>
                  <li><a href="{% url 'register' %}">注 册</a></li>
              {% endif %}
          </ul>
        </div><!-- /.navbar-collapse -->
      </div><!-- /.container-fluid -->
    </nav>
{% block content %} {% endblock %}

{% block js %}{% endblock %}
</body>
</html>


# 使用
{% extends 'bases/base.html' %}

{% block tilte %}登录{% endblock %}

{% block content %}
<div class="account">
    <h1 class="text-center h1">登录</h1>
    <div>
        <form id="registerForm" method="post" novalidate>
            {% csrf_token %}
            {% for field in form %}
            {% if field.name == 'code' %}
                <div class="form-group">
                    <label for={{ field.auto_id }}>{{ field.label }}</label>
                    <div class="clearfix">
                        <div class="col-md-6" style="padding-left: 0">{{ field }}</div>
                        <div class="col-md-6" style="padding-right: 0"><button id="code" class="btn btn-default" style="width: 150px">点击获取验证码</button></div>
                    </div>
                </div>
            {% else %}
                <div class="form-group">
                    <label for={{ field.auto_id }}>{{ field.label }}</label>
                    {{ field }}
                    <span style="color:red;position: absolute" class="pull-right">{{ field.errors }}</span>
                </div>
            {% endif %}
        {% endfor %}
        </form>
        <button class="btn btn-primary" style="width: 150px" id="submit">登录</button>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                <a href="{% url 'login' %}"> 账号密码登录 </a>
    </div>
</div>
{% endblock %}


{% block js %}
    <script>
        let time=60;
        let $codeBtn=$('#code');
        let $phoneEle=$('#id_phone');
        let $submitBtn=$('#submit');
        let $registerForm=$('#registerForm');
        $codeBtn.click(function ()
            {
            let $phoneNumber=$phoneEle.val();
            $codeBtn.prop('disabled',true)
            let remind=setInterval(function (){
                                $codeBtn.text(time+'秒重新发送')
                                time=time-1;
                                if (time < 1)
                                {
                                    clearInterval(remind)
                                    $codeBtn.text('点击获取验证码').prop('disabled',false)
                                }
                            },1000)
            $.ajax(
                {
                    url:'/sms/',
                    type:'post',
                    data: {
                        'phone': $phoneNumber, 'method':'login','csrfmiddlewaretoken':'{{csrf_token}}'
                        },
                        success: function (response) {
                            if(! response.code)
                            {
                                 alert(response.msg.phone)
                                 $phoneEle.val('')
                            }
                            else{
                                alert('验证码发送成功')
                            }
                        }
                }
                )
            }
        )

        $submitBtn.click(function (e){
            $.ajax({
                url:'/smslogin/',
                type: 'post',
                data: $registerForm.serialize(),
                success:function (res){
                    if(res.code===1)
                    {
                        location.href=res.url
                    }
                    else
                    {
                        if (res.msg.code)
                        {
                           alert(res.msg.code)
                        }
                        else
                        {
                            alert(res.msg.phone)
                        }
                    }
                }
            })
        })

        $('input').click(function (){
            $(this).next().text('').parent().removeClass('has-error')
        })
    </script>
{% endblock %}


#1、标签extends必须放在首行base.html中block越多可定制性越强

#2、include仅仅只是完全引用其他模板文件而extends却可以搭配block在引用的基础上进行扩写

#3、变量{{ block.super }} 可以重用父类的内容然后在父类基础上增加新内容而不是完全覆盖

#4、为了提升可读性我们可以给标签{% endblock %} 起一个名字 。例如
    {% block content %}
    ...
    {% endblock content %}  
#5、在一个模版中不能出现重名的block标签。

inclusion_tag

  1. 当页面上某一块区域的内容需要在多个页面上展示的使用并且该区域的内容需要通过传参数才能正常显示那么可以优先考虑inclusion_tag模块。
  2. 定义inclusion_tag与定义过滤器和标签inclusion_tag也是tag的一种相同
# 需要传入一个可以渲染的模板
@register.inclusion_tag('inclusion_tags/menu_list.html')
def all_menu_list(request):
    menu_list = [
        {'title': '概览', 'url': reverse("dashboard", kwargs={'pk': request.project.id})},
        {'title': '问题', 'url': reverse("issues", kwargs={'pk': request.project.id})},
        {'title': '统计', 'url': reverse("statistics", kwargs={'pk': request.project.id})},
        {'title': 'wiki', 'url': reverse("wiki", kwargs={'pk': request.project.id})},
        {'title': '文件', 'url': reverse("file", kwargs={'pk': request.project.id})},
        # {'title': '设置', 'url': reverse("settings", kwargs={'pk': request.project.id})}
    ]
    for item in menu_list:
        current_url = request.path_info  # type:str
        if current_url.startswith(item['url']):
            item['style'] = "color:white"

    return {'menu_list': menu_list}

# 渲染该模板
{% for item in menu_list %}
<li><a href="{{ item.url }}" {% if item.style %}style="{{ item.style }}" {% endif %}>{{ item.title }}</a></li>
{% endfor %}

# 使用
# 加载定义的inclusion_tag.py
{% load inclusion_tag %}

{% if request.project %}
<ul class="nav navbar-nav">
    {% all_menu_list request=request %}
</ul>
{% endif %}

ORM模型层

ORM全称Object Relational Mapping即对象关系映射是在pymysq之上又进行了一层封装对于数据的操作我们无需再去编写原生sql取代代之的是基于面向对象的思想去编写类、对象、调用相应的方法等ORM会将其转换/映射成原生SQL然后交给pymysql执行。
在这里插入图片描述

在这里插入图片描述

单表操作

创建models.py

from django.db import models

class UserInfo(models.Model):
    username = models.CharField(verbose_name='用户名', max_length=32, unique=True)
    password = models.CharField(verbose_name='密码', max_length=32)
    email = models.EmailField(verbose_name='邮箱', unique=True)
    phone = models.CharField(verbose_name='手机号', unique=True, max_length=11)
    # identity = models.ForeignKey(verbose_name='账户类型', to='PricePolicy', on_delete=models.DO_NOTHING)
    project_num = models.IntegerField(verbose_name='创建的项目数量', default=0)

    def __str__(self):
        return self.username

在这里插入图片描述

配置数据库连接

# 可能有人考虑到mysql集群分库分表mycat可以将mysql集群的ip+port隐藏对外提供一个service(ip+port)所以此处只需要配一个ip+port就行
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', # 使用mysql数据库
        'NAME': 'db1',          # 要连接的数据库
        'USER': 'root',         # 链接数据库的用于名
        'PASSWORD': '',         # 链接数据库的用于名                  
        'HOST': '127.0.0.1',    # mysql服务监听的ip  
        'PORT': 3306,           # mysql服务监听的端口  
        'ATOMIC_REQUEST': True, #设置为True代表同一个http请求所对应的所有sql都放在一个事务中执行 
                                #(要么所有都成功要么所有都失败)这是全局性的配置如果要对某个
                                #http请求放水然后自定义事务可以用non_atomic_requests修饰器 
        'OPTIONS': {
            "init_command": "SET storage_engine=INNODB", #设置创建表的存储引擎为INNODB
        }
    }
}

在链接mysql数据库前必须事先创建好数据库下载连接

# 创建数据库
create database db1;

# 下载mysqlclient,配置了镜像就可以下载否则会下载失败
mysqlclient==2.0.3
pip install mysqlclient

在这里插入图片描述

配置APP

其实按照django1.x的版本给2.x注册app也可以识别

# django1.x版本
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01',
]

# django2.x版本
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config', 
]

打印sql-orm

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

迁移数据库

$ python manage.py makemigrations
$ python manage.py migrate

# 注意
# 1、makemigrations只是生成一个数据库迁移记录的文件而migrate才是将更改真正提交到数据库执行
# 2、数据库迁移记录的文件存放于app01下的migrations文件夹里
# 3、使用命令python manage.py showmigrations可以查看没有执行migrate的文件

修改字段

# 一增加字段
#1.1、在模型类Employee里直接新增字段强调对于orm来说新增的字段必须用default指定默认值
publish = models.CharField(max_length=12,default='人民出版社',null=True)
#1.2、重新执行那两条数据库迁移命令


# 二删除字段
#2.1 直接注释掉字段
#2.2 重新执行那两条数据库迁移命令

# 三修改字段
#2.1 将模型类中字段修改
#2.2 重新执行那两条数据库迁移命令

操作记录

新增

# 1、用模型类创建一个对象一个对象对应数据库表中的一条记录
obj = Employee(name="Egon", gender=0, birth='1997-01-27', department="财务部", salary=100.1)
# 2、调用对象下的save方法即可以将一条记录插入数据库
obj.save()

# 2、每个模型表下都有一个objects管理器用于对该表中的记录进行增删改查操作其中增加操作如下所示
obj = Employee.objects.create(name="Egon", gender=0, birth='1997-01-27', department="财务部", salary=100.1)

# 3、当form继承了forms.ModelForm时可以调用form.save()返回一个instance对象
if request.method == 'GET':
    form = RegisterForm()
    return render(request, 'register.html', locals(), status=200)
elif request.method == 'POST':
    res = ApiResponse()
    form = RegisterForm(data=request.POST)
    if form.is_valid():
        instance = form.save()

查询

# 1. get(**kwargs)
# 1.1: 有参参数为筛选条件
# 1.2: 返回值为一个符合筛选条件的记录对象(有且只有一个)如果符合筛选条件的对象超过一个或者没有都会抛出错误。
obj=Employee.objects.get(id=1)
print(obj.name,obj.birth,obj.salary)

# 2、first()
# 2.1无参
# 2.2返回查询出的第一个记录对象
obj=Employee.objects.first() # 在表所有记录中取第一个
print(obj.id,obj.name)

# 3、last()
# 3.1: 无参
# 3.2: 返回查询出的最后一个记录对象
obj = Employee.objects.last() # 在表所有记录中取最后一个
print(obj.id, obj.name)

# 4、count():
# 4.1无参
# 4.2返回包含记录对象的总数量
res = Employee.objects.count() # 统计表所有记录的个数
print(res)

# 注意如果直接打印Employee的对象将没有任何有用的提示信息可以在模型类中定义__str__来进行定制
class Employee(models.Model):
    ......
	# 在原有的基础上新增代码如下
	
    def __str__(self):
        return "<%s:%s>" %(self.id,self.name)


------------------------------------------------------------------------------------
下述方法的返回值均为QuerySet类型的对象QuerySet对象中包含了查询出的多个记录对象
# 1、filter(**kwargs):
# 1.1有参参数为过滤条件
# 1.2返回值为QuerySet对象QuerySet对象中包含了符合过滤条件的多个记录对象
queryset_res=Employee.objects.filter(department='技术部')
# print(queryset_res) # 输出 <QuerySet [<Employee: <2:Kevin>>, <Employee: <5:Jack>>, <Employee: <6:Robin>>]>

# 2、exclude(**kwargs)
# 2.1: 有参参数为过滤条件
# 2.2: 返回值为QuerySet对象QuerySet对象中包含了不符合过滤条件的多个记录对象
queryset_res=Employee.objects.exclude(department='技术部')

# 3、all()
# 3.1无参
# 3.2返回值为QuerySet对象QuerySet对象中包含了查询出的所有记录对象
queryset_res = Employee.objects.all() # 查询出表中所有的记录对象

# 4、order_by(*field):
# 4.1有参参数为排序字段可以指定多个字段在字段1相同的情况下可以按照字段2进行排序以此类推默认升序排列在字段前加横杆代表降序排如"-id"
# 4.2返回值为QuerySet对象QuerySet对象中包含了排序好的记录对象
queryset_res = Employee.objects.order_by("salary","-id") # 先按照salary字段升序排如果salary相同则按照id字段降序排

# 5、values(*field)
# 5.1有参参数为字段名可以指定多个字段
# 5.2返回值为QuerySet对象QuerySet对象中包含的并不是一个个的记录对象而上多个字典字典的key即我们传入的字段名
queryset_res = Employee.objects.values('id','name')
print(queryset_res) # 输出<QuerySet [{'id': 1, 'name': 'Egon'}, {'id': 2, 'name': 'Kevin'}, ......]>
print(queryset_res[0]['name']) # 输出Egon

# 6、values_list(*field):
# 6.1有参参数为字段名可以指定多个字段
# 6.2返回值为QuerySet对象QuerySet对象中包含的并不是一个个的记录对象而上多个小元组字典的key即我们传入的字段名
queryset_res = Employee.objects.values_list('id','name')
print(queryset_res) # 输出<QuerySet [(1, 'Egon'), (2, 'Kevin'),), ......]>
print(queryset_res[0][1]) # 输出Egon
QuerySet对象
# 过滤出符合条件的多个记录对象然后存放到QuerySet对象中
queryset_res=Employee.objects.filter(department='技术部') 
# 按照索引从QuerySet对象中取出第一个记录对象
obj=queryset_res[0]
print(obj.name,obj.birth,obj.salary)
链式处理
# 简单示范
res=Employee.objects.filter(gender=1).order_by('-id').values_list('id','name')
print(res) # 输出<QuerySet [(6, 'Robin'), (5, 'Jack'), (4, 'Tom'), (2, 'Kevin')]>
其他查询API
# 1、reverse():
# 1.1无参
# 1.2对排序的结果取反返回值为QuerySet对象
queryset_res = Employee.objects.order_by("salary", "-id").reverse()

# 2、exists():
# 2.1无参
# 2.2返回值为布尔值如果QuerySet包含数据就返回True否则返回False
res = Employee.objects.filter(id=100).exists()
print(res)  # 输出False

# 3、distinct():
# 3.1如果使用的是Mysql数据库,那么distinct()无需传入任何参数
# 3.2从values或values_list的返回结果中剔除重复的记录对象返回值为QuerySet对象
res = Employee.objects.filter(name='Egon').values('name', 'salary').distinct()
print(res) # 输出<QuerySet [{'name': 'Egon', 'salary': Decimal('100.1')}]>

res1 = Employee.objects.filter(name='Egon').values_list('name', 'salary').distinct()
print(res1) # 输出<QuerySet [('Egon', Decimal('100.1'))]>
双下划线查询
模板
filter(字段__关键字=)filter(id_in=[1,2,3])查询id123的

关键字
gt=1 大于1
lt=1 小于1
gte=1 大于等于1
let=1 小于等于1
in=[1][1]range=[1,10][1,2,3,4,5,6,7,8,9,10]# 模糊查询
contains="坤" 字段中有"坤"的
icontains="坤" 忽略大小写如上
startwith="坤" 坤字开头的
endwith="坤" 坤字结尾的
month="月份" 月份
year="年份" 年份

在这里插入图片描述

F与Q查询

F用于比较Q用于拼接条件

# 一张书籍表中包含字段评论数commentNum、收藏数keepNum要求查询评论数大于收藏数的书籍
from django.db.models import F
Book.objects.filter(commnetNum__lt=F('keepNum'))

# 查询评论数大于收藏数2倍的书籍
from django.db.models import F
Book.objects.filter(commnetNum__lt=F('keepNum')*2)


# 修改操作也可以使用F函数,比如将每一本书的价格提高30元
ook.objects.all().update(price=F("price")+30) 

# 拼接一个字符串
from django.db.models.functions import Concat
from django.db.models import Value
Employee.objects.filter(nid__lte=3).update(name=Concat(F('name'),Value('_sb')))
# 可以将条件传给类Q来实例化出一个对象Q的对象可以使用& 和| 操作符组合起来&等同于and|等同于or
from django.db.models import Q
Employee.objects.filter(Q(id__gt=5) | Q(name="Egon"))

# Q 对象可以使用~ 操作符取反相当于NOT
from django.db.models import Q
Employee.objects.filter(~Q(id__gt=5) | Q(name="Egon"))

# 当过滤条件中既有or又有and则需要混用Q对象与关键字参数但Q 对象必须位于所有关键字参数的前面
from django.db.models import Q
Employee.objects.filter(Q(id__gt=5) | Q(name="Egon"),salary__lt=100)
聚合
  1. 聚合查询aggregate()是把所有查询出的记录对象整体当做一个组。
  2. 分组是使用特定的条件将元数据进行划分为多个组。
  3. 聚合是对每个分组中的数据执行某些操作最后将计算结果进行整合。
from django.db.models import Avg, Max, Sum, Min, Max, Count # 导入聚合函数

# 1. 调用objects下的aggregate()方法会把表中所有记录对象整体当做一组进行聚合
res1=Employee.objects.aggregate(Avg("salary")) # select avg(salary) as salary__avg from app01_employee;
print(res1) # 输出{'salary__avg': 70.73}

# 2、aggregate()会把QuerySet对象中包含的所有记录对象当成一组进行聚合
res2=Employee.objects.all().aggregate(Avg("salary")) # select avg(salary) as salary__avg from app01_employee;
print(res2) # 输出{'salary__avg': 70.73}

res3=Employee.objects.filter(id__gt=3).aggregate(Avg("salary")) # select avg(salary) as salary__avg from app01_employee where id > 3;
print(res3) # 输出{'salary__avg': 71.0}

# aggregate()的返回值为字典类型字典的key是由”聚合字段的名称___聚合函数的名称”合成的如`salary__avg`
# 若想定制字典的key名我们可以指定关键参数
res1=Employee.objects.all().aggregate(avg_sal=Avg('salary')) # select avg(salary) as avg_sal from app01_employee;
print(res1) # 输出{'avg_sal': 70.73} # 关键字参数名就会被当做字典的key

# 想得到多个聚合结果那就需要为aggregate传入多个参数
res1=Employee.objects.all().aggregate(nums=Count('id'),avg_sal=Avg('salary'),max_sal=Max('salary')) 
# 相当于SQLselect count(id) as nums,avg(salary) as avg_sal,max(salary) as max_sal from app01_employee;
print(res1) # 输出{'nums': 10, 'avg_sal': 70.73, 'max_sal': Decimal('200.3')}
分组

分组查询annotate()相当于sql语句中的group by是在分组后对每个组进行单独的聚合需要强调的是在进行单表查询时annotate()必须搭配values()使用values(“分组字段”).annotate(聚合函数)

# 查询每个部门下的员工数
res=Employee.objects.values('department').annotate(num=Count('id')) 
# select department,count(id) as num from app01_employee group by department;
print(res) 
# 输出<QuerySet [{'department': '财务部', 'num': 2}, {'department': '技术部', 'num': 3}, {'department': '运营部', 'num': 2}]>

跟在annotate前的values方法是用来指定分组字段即group by后的字段而跟在annotate后的values方法则是用来指定分组后要查询的字段即select 后跟的字段

res=Employee.objects.values('department').annotate(num=Count('id')).values('num')
# select count(id) as num from app01_employee group by department;
print(res)
# 输出<QuerySet [{'num': 2}, {'num': 3}, {'num': 2}]>

跟在annotate前的filter方法表示where条件跟在annotate后的filter方法表示having条件

# 查询男员工数超过2人的部门名
res=Employee.objects.filter(gender=1).values('department').annotate(male_count=Count("id")).filter(male_count__gt=2).values('department')
print(res) # 输出<QuerySet [{'department': '技术部'}]>
# 解析
# 1、跟在annotate前的filter(gender=1) 相当于 where gender = 1先过滤出所有男员工信息
# 2、values('department').annotate(male_count=Count("id")) 相当于group by department对过滤出的男员工按照部门分组然后聚合出每个部门内的男员工数赋值给字段male_count
# 3、跟在annotate后的filter(male_count__gt=2) 相当于 having male_count > 2,会过滤出男员工数超过2人的部门
# 4、最后的values('department')代表从最终的结果中只取部门名

更新

# 单条
# 1、获取记录对象
obj=Employee.objects.filter(name='Egon')[0]
# 2、修改记录对象属性的值
obj.name='EGON'
obj.gender=1
# 3、重新保存
obj.save()
----------------------------------
# 多条
queryset_obj=Employee.objects.filter(id__gt=5)
rows=queryset_obj.update(name='EGON',gender=1)

删除

# 单条
obj=Employee.objects.first()
obj.delete()

-----------------
# 多条
queryset_obj=Employee.objects.filter(id__gt=5)
rows=queryset_obj.delete()
# 全部
Employee.objects.all().delete()
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: go

“django框架【待续】” 的相关文章