django框架【待续】

news2024/10/7 8:22:34

目录

  • 简介
    • MVC与MTV模型
      • MVC
      • MTV
    • 创建项目
    • 目录
    • 生命周期
    • 静态文件配置(无用)
  • 启动django[启动](https://www.cnblogs.com/xiaoyuanqujing/articles/11902303.html)
  • 路由
    • 分组
      • 无名分组
      • 有名分组
    • 路由分发
    • 反向解析
    • 反向解析结合分组
    • 名称空间
    • re_path与path
      • 自定义转换器
  • 视图
    • HttpRequest
      • 常用方法
  • HttpResponse
    • Json
  • FBV和CBV
  • 模板(前后端分离不用看)
    • 变量
    • 过滤器
      • 循环
      • 分支
      • csrf
      • with
      • 自定义过滤器和标签
    • 模板的导入和继承
    • 模板的继承\派生之extends标签、block标签
    • inclusion_tag
  • ORM模型层
    • 单表操作
      • 创建models.py
      • 配置数据库连接
      • 在链接mysql数据库前,必须事先创建好数据库,下载连接
      • 配置APP
      • 打印sql-orm
      • 迁移数据库
      • 修改字段
      • 操作记录
        • 新增
        • 查询
          • QuerySet对象:
          • 链式处理:
          • 其他查询API:
          • 双下划线查询
          • F与Q查询
          • 聚合
          • 分组
        • 更新
        • 删除

简介

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可以传入一些指定参数,如:防止将中文字符串转为ascii,ensure_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')) 
# 相当于SQL:select 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()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/169476.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么普通人赚钱这么难?普通人的赚钱之路在哪里

前几天听一个老家的朋友说辛辛苦苦一整年&#xff0c;发现并没有赚到什么钱。付出与收入不成正比。首先要知道勤奋、努力并不一定就能够赚到钱像送外卖的&#xff0c;工地上班的&#xff0c;厂里上班的哪个不勤奋但他们即使非常努力工作一个月&#xff0c;扣除基本开支&#xf…

这是一篇知识帖:终于能明白云原生技术的概念和可落地的应用分享

随着云计算的发展和普及&#xff0c;云原生概念的热度也越来越高&#xff0c;到底什么是云原生&#xff1f;和我们日常工作有什么关系&#xff1f;本文是向大家介绍云原生技术的概念和要点&#xff0c;帮助大家快速了解和学习云原生&#xff0c;&#xff0c;便于大家了解工作的…

【PHP了解】PHP脚本语言基础

PHP PHP 文件的默认文件扩展名是 “.php”PHP 文件通常包含 HTML 标签和一些 PHP 脚本代码 例&#xff1a; <!DOCTYPE html> <html> <body><?php xxxxxxxxxxxxxxxxx; ?></body> </html>语法 代码框架 <?php 开始&#xff0c;以 ?…

DW动手学数据分析Task1:数据加载及探索性数据分析

目录1 数据载入1.1 载入数据1.2 read_csv和read_table的区别1.3 逐块读取1.4 修改表头和索引2 数据初步观察2.1 查看数据基本信息2.2 保存数据3 pandas数据结构和列操作3.1 Series 和DataFrame3.2 查看 DataFrame数据每列的名称和值3.3 DataFrame 的列删除和隐藏4 数据筛选4.1 …

如何合并pdf?来看这几个方法!

我们经常在整理文件的时候经常会选择将一些常用文件的格式改为pdf&#xff0c;因为这种文件格式就算发送给了别人&#xff0c;别人也不能随意的修改文件内容&#xff0c;这就保证了我们的文件内容的隐私安全。不过&#xff0c;对于处理pdf文件&#xff0c;我们还是会面临一些不…

c++ dynamic_cast,static_cast,const_cast,reinterpret_cast四种cast用法整理

在C中主要分为四种cast&#xff0c;分别是&#xff1a;static_cast、dynamic_cast、const_cast和reinterpret_cast dynamic_cast动态类型转换 首先&#xff0c;我们明确基类和派生类之间的两个转换术语 向上转换(upcast)&#xff1a;派生类向基类转换 向下站还(downcast)&…

证券服务应用评测系列:海通e海通财发布9.0版本,探索证券APP持续提升用户体验

易观分析&#xff1a;APP作为券商机构线上服务的入口&#xff0c;其活跃用户规模和运营水平在一定程度上能够体现机构战略定位、品牌运营、科技创新、用户竞争等综合实力。易观分析认为&#xff0c;如何围绕用户进行创新&#xff0c;利用技术将壁垒升高&#xff0c;打造具有品牌…

PuTTY installation directory must be on a local hard drive

putty安装问题 问题描述&#xff1a;安装时候报错&#xff0c;installation directory must be on a local hard drive 解决方案&#xff0c;无法解决。 很多人使用了这个方法解决了。但是我解决不了。 1、如果是exe类型的&#xff0c;直接右击以管理员身份运行。 2、如果是…

常用的文件类型大全(建议收藏)

文件格式格式图标格式概况mpg文件mpg是一种常见的视频格式,mpeg标准主要有以下五个,MPEG-1、MPEG-2、MPEG-4、MPEG-7及MPEG-21等。mpg有可能是基于MPEG-1(VCD)或MPEG-2torrent文件torrent文件(扩展名为.torrent)包含了一些BT下载所必须的信息,根据BitTorrent协议,文件发布者…

opencv文字旋转 putText旋转90°

文章目录● opencv文字旋转 putText旋转901. cv::getTextSize获取文字的尺寸textSize2. 设置文字图像textImg尺寸格式3. 将文字绘制在文字图像textImg上4. 对文字图像textImg进行旋转5. 在原图img上设置用来放置文字图像textImg的roi区域5.1 矩形区域roi5.2 行列范围roi6. 将文…

Swin-Unet: Unet-like Pure Transformer for Medical Image Segmentation论文解读

Swin-Unet: Unet-like Pure Transformer for Medical Image Segmentation 论文&#xff1a;[2105.05537] Swin-Unet: Unet-like Pure Transformer for Medical Image Segmentation (arxiv.org) 代码&#xff1a;HuCaoFighting/Swin-Unet: The codes for the work “Swin-Unet:…

LiveNVR监控流媒体Onvif/RTSP功能功能-支持GB35114接入国标流媒体平台接入说明

LiveNVR监控流媒体Onvif/RTSP功能功能-支持GB35114接入国标流媒体平台接入说明1、GB35114注册2、LiveNVR通过GB35114接入LiveGBS2.1、开启LiveGBS 35114功能2.2、 获取设备端证书给平台2.3、LiveGBS白名单中添加需要接入的国密设备2.4、导出LiveGBS平台端证书给设备&#xff08…

管理学实验的设计与实践结课作业

一、不同组织结构类型对组织的沟通效率会带来影响&#xff0c;这也是在组织结构设计过 程需要考虑的重要内容&#xff0c;但对于两者的关系无法准确界定。如在直线职能制的组织结构中&#xff0c;管理幅度和深度是影响是影响沟通效率的主要因素。请设计实验来验证分析直线职能制…

手动部署SQL审计平台Archery(连接mysql8.x)

安装Python和virtualenv从1.9.0版本开始&#xff0c;将不再支持python3.7及以下版本&#xff0c;手动安装也将使用3.9版本作为样例&#xff0c;移除yum安装方式# 安装依赖 yum install libffi-devel wget gcc make zlib-devel openssl openssl-devel ncurses-devel openldap-de…

分享81个PHP源码,总有一款适合您

PHP源码 分享81个PHP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 81个PHP源码下载链接&#xff1a;https://pan.baidu.com/s/18jo0nrQ6tCf-e1PAylItmQ?pwd3rxz 提取码&#xff…

Mysql之多表查询

简单测试 由于我们之前没学过多表查询 我们简单测试一下初学者认为的多表查询 SELECT * FROM emp,dept这是我们的输出结果 其实不难发现 1.返回的结果就是&#xff0c;第一张表一个个数据取出然后和第二张表所有组合 最后返回的是含有两张表的所有列 2.一共返回的记录数第一章…

【ES6篇新语法】(1)ES6介绍及常量声明、对象数组,附实例详解

【写在前面】 应一些粉丝的要求&#xff0c;整理一些ES6相关的文章&#xff0c;因此借这个岁末之际&#xff0c;给大家整理一篇ES6的相关的博客&#xff0c;不管是面试还是在实际的编程上&#xff0c;我们都常提到ES6这个词&#xff0c;可能前几年比较热门&#xff0c;希望大家…

焊接变形的原因以及防止焊接变形的措施

焊接变形的原因   在焊接过程中对焊件进行了局部的、不均匀的加热是产生焊接应力及变形的原因。焊接时焊缝和焊缝附近受热区的金属发生膨胀&#xff0c;由于四周较冷的金属阻止这种膨胀&#xff0c;在焊接区域内就发生压缩应力和塑性收缩变形&#xff0c;产生了不同程度的横向…

喜报!西门子低代码连续四年获评Gartner企业低代码应用平台魔力象限领导者

点击下载最新发布的Gartner魔力象限图™https://www.mendix.com/resources/gartner-magic-quadrant-for-low-code-application-platforms/?utm_mediumpr&utm_campaignMQ&utm_sourceMQ#formWrapper 想象一下&#xff0c;一个为持续数字化转型做好准备的组织会是什么样…

EXTI中断以及系统滴答定时器SysTick的配置和使用

前言 EXTI中断来判断按键按下 EXTI即外部中断/事件控制器&#xff0c;总共支持19个中断/事件请求。每一条中断线都有独立的使能和产生中断后的标志位。 上图可见&#xff0c;中断/时间线0-15&#xff0c;总共16条线分配给了IO&#xff0c;通过设置AFIO的AFIO_EXTICR1、AFIO_…