• django框架基础


    内容:

    1.课前复习

    2.django路由规则

    3.django视图函数

    4.django ORM

    5.模板

    django预备知识:https://www.cnblogs.com/wyb666/p/9444150.html

    django基础参考:http://www.cnblogs.com/wupeiqi/articles/5237704.html

    一、课前复习

    1.HTTP协议消息格式

     1    请求(request):
     2         请求方法 路径 HTTP/1.1
    
     3         k1:v1
    
     4         ...
    
     5         
    
     6         请求体        <-- 可以有,可以没有
     7     
     8     响应(response):
     9         HTTP/1.1 状态码 状态描述符
    
    10         k1:v1
    
    11         Content-Type: text/html; charset=utf8
    
    12         
    
    13         响应正文       <-- HTML内容

    2.python的web框架本质

    • 收发socket消息 --> 按照HTTP协议消息格式去解析消息
    • 路径和要执行的函数的对应关系 --> 主要的业务逻辑
    • 字符串替换 --> 模板(特殊符号 --> 数据)

    3.一个完整的请求过程:

    • 启动服务端,等待客户端(用户的浏览器)来连接
    • 在浏览器地址栏输入URL,与服务端建立连接,浏览器发送请求
    • 服务端收到请求消息,解析请求消息,根据路径和函数的对应关系,找到将要执行的函数
    • 执行函数,打开HTML文件,进行字符串替换,得到一个最终要返回的HTML内容
    • 按照HTTP协议的消息格式要求,把HTML内容回复给用户浏览器(发送响应)
    • 浏览器收到响应的消息之后,按照HTML的规则渲染页面.
    • 关闭连接

     

    二、django路由系统

    在django程序中,可以通过urls.py文件对所有的url进行任务的分配,根据路由规则的定义选择不同的业务处理函数进行处理,urls.py中规定的对应规则可以说是路由系统(URLconf)

    关于路由规则的详细内容:http://www.cnblogs.com/liwenzhou/p/8271147.html

    1.基本路由规则

    基本格式(1.x版本):

    1 from django.conf.urls import url
    2 
    3 urlpatterns = [
    4      url(正则表达式, views视图函数,参数,别名),
    5 ]
    • 正则表达式:一个正则表达式字符串
    • views视图函数:一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
    • 参数:可选的要传递给视图函数的默认参数(字典形式)
    • 别名:一个可选的name参数

    Django 2.0版本路由系统的写法:

    1 from django.urls import path
    2 
    3 urlpatterns = [
    4     path('articles/', views.articles),
    5     path('articles/<int:year>/', views.year_archive),
    6     path('articles/<int:year>/<int:month>/', views.month_archive),
    7     path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
    8 ]

    2.路由系统正则表达式详解

    基础样例 - 分组匹配:

    1 from django.conf.urls import url
    2 from . import views
    3 
    4 urlpatterns = [
    5     url(r'^articles/2003/$', views.special_case_2003),
    6     url(r'^articles/([0-9]{4})/$', views.year_archive),
    7     url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
    8     url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
    9 ]

    注意:

    • urlpatterns中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则不再继续
    • 若要从URL中捕获一个值,只需要在它周围放置一对圆括号(分组匹配)
    • 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles
    • 每个正则表达式前面的'r' 是可选的但是建议加上

    上面的示例使用简单的正则表达式分组匹配(通过圆括号)来捕获URL中的值并以位置参数形式传递给视图;在更高级的用法中,可以使用分组命名匹配的正则表达式组来捕获URL中的值并以关键字参数形式传递给视图

    在Python的正则表达式中,分组命名正则表达式组的语法是(?P<name>pattern),其中name是组的名称,pattern是要匹配的模式

    分组命名匹配:

    1 from django.conf.urls import url
    2 from . import views
    3 
    4 urlpatterns = [
    5     url(r'^articles/2003/$', views.special_case_2003),
    6     url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    7     url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    8     url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
    9 ]

    这个实现与前面的示例完全相同,只有一个细微的差别:捕获的值作为关键字参数而不是位置参数传递给视图函数,另外注意无论哪种方法每个在路由中捕获的参数都作为一个普通的Python字符串传递给视图

    另外views.py中可以给视图函数的参数设置默认值:

    3.django路由分发

    1个Django 项目里面有多个APP目录大家共有一个 url容易造成混淆,于是路由分发让每个APP的拥有了自己单独的url:

    项目目录下的urls.py:

    1 from django.conf.urls import url, include
    2 from django.contrib import admin
    3 
    4 urlpatterns = [
    5     # 分发到APP的路由
    6     url(r'^basic/', include('basic.urls')),
    7     url(r'^app1/', include('app1.urls')),
    8     url(r'^app2/', include('app2.urls')),
    9 ]

    basic这个APP下的urls.py:

    1 from django.conf.urls import url
    2 from basic import views
    3 
    4 urlpatterns = [
    5     url(r'^index/', views.index),
    6     url(r'^login/', views.login),
    7     url(r'^register/', views.register),
    8 ]

    完成上述设置后,basic这个APP的路由只需在自己APP下的urls.py配置即可,访问时前面带上basic路径即可

    4.命名URL和URL反向解析

    (1)前言

    在使用Django 项目时,一个常见的需求是获得URL的最终形式,以用于嵌入到生成的内容中(视图中和显示给用户的URL等)或者用于处理服务器端的导航(重定向等)。
    人们强烈希望不要硬编码这些URL(费力、不可扩展且容易产生错误)或者设计一种与URLconf 毫不相关的专门的URL 生成机制,因为这样容易导致一定程度上产生过期的URL。
    换句话讲,需要的是一个DRY 机制。除了其它有点,它还允许设计的URL 可以自动更新而不用遍历项目的源代码来搜索并替换过期的URL。
    获取一个URL 最开始想到的信息是处理它视图的标识(例如名字),查找正确的URL 的其它必要的信息有视图参数的类型(位置参数、关键字参数)和值。
    Django 提供一个办法是让URL 映射是URL 设计唯一的地方。你填充你的URLconf,然后可以双向使用它:

    • 根据用户/浏览器发起的URL 请求,它调用正确的Django 视图,并从URL 中提取它的参数需要的值
    • 根据Django 视图的标识和将要传递给它的参数的值,获取与之关联的URL

    (2)命名URL和URL反向解析

    前言中第一种方式是我们在前面的章节中一直讨论的用法,是直接获得url的最终形式,可以称之为命名URL

    而第二种方式叫做反向解析URL或者简单的URL 反查
    在需要URL 的地方,对于不同层级,Django 提供不同的工具用于URL 反查:

    • 在模板中:使用url模板标签。
    • 在Python 代码中:使用django.core.urlresolvers.reverse() 函数。
    • 在更高层的与处理Django 模型实例相关的代码中:使用get_absolute_url() 方法。

    反向解析URL本质上就是给url匹配模式起别名,然后用过别名拿到具体的URL路径

    用法如下:

     1 起别名: 在url匹配模式中,定义name="别名"
     2 
     3 1. 在模板语言里面使用:
     4 {% url "别名" %} --> 得到具体的URL路径
     5 
     6 
     7 2. 在视图中如何使用:
     8 from django.urls import reverse
     9 
    10 reverse("别名") --> 得到具体的URL路径
    11 
    12 
    13 3. 如何传参数?
    14 模板语言中:
    15 {% url "别名" 2018 "nb" %}
    16 
    17 视图函数中
    18 传位置参数:    reverse("别名", args=(2018, "nb"))
    19 传关键字参数: reverse("别名" kwargs={"year": 2018, "title": "nb"})
    20 
    21 
    22 4. namespace
    23 为了防止不同的app下面的url匹配模式有重复的别名,可以给app起别名

    (3)实例如下:

    urls.py:

    1 urlpatterns = [
    2     url(r'^admin/', admin.site.urls, name="admin"),
    3     # 分发到APP的路由
    4     url(r'^basic/', include('basic.urls', namespace="basic")),
    5 ]
    1 urlpatterns = [
    2     url(r'^user_list/', views.user_list, name="user_list"),
    3     url(r'^add_user/', views.add_user, name="add_user"),
    4 ]

    views.py:

     1 def user_list(request):
     2     res = models.UserInfo.objects.all()
     3     return render(request, "user/user_list.html", {"user_list": res})
     4 
     5 def add_user(request):
     6     err_msg = ""
     7     if request.method == "POST":
     8         new_name = request.POST.get("username", None)
     9         new_pwd = request.POST.get("password", None)
    10         if new_name and new_pwd:
    11             models.UserInfo.objects.create(username=new_name, password=new_pwd)
    12             return redirect(reverse("basic:user_list"))
    13         else:
    14             err_msg = "用户名或密码不能为空!"
    15     return render(request, "user/add_user.html", {"error": err_msg})

    HTML:

    1 <a class="btn btn-default" href="{% url "basic:add_user" %}" role="button">添加用户</a>

    三、django视图系统

    视图系统简称视图,可以是一个简单的Python 函数,它接受Web请求并且返回Web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西都可以。无论视图本身包含什么逻辑,都要返回响应。

    约定将视图放置在项目或APP目录中的名为views.py中(一般为了模块化都是放在APP的views里)

    关于视图系统详细:http://www.cnblogs.com/liwenzhou/articles/8305104.html

    1.最简单的视图

    不论什么视图都包含两个对象:

    • request:用户请求的相关所有信息,在django中是HttpRequest对象
    • response:响应信息(字符串 or HTML or 图片、、、),在django中是HttpResponse对象

    最简单的视图:

    1 from django.shortcuts import HttpResponse
    2 
    3 def easy_view(request):
    4     # 每个视图函数都使用HttpRequest对象作为第一个参数,并且通常称之为request
    5     return HttpResponse("hello world!")

    视图函数,围绕着两个对象进行:HttpResponse和HttpRequest

    2.HttpRequest

    (1)属性

     1 request.path       获取访问文件路径
     2 request.path_info       获取用户请求的路径(不包含IP和端口和URL参数)
     3 request.method      获取请求中使用的HTTP方式(POST/GET)
     4 request.body      含所有请求体信息 是bytes类型
     5 request.GET       GET请求的数据(类字典对象)  请求头中的url中?后面拿值
     6 request.POST       POST请求的数据(类字典对象) 请求体里拿值
     7 
     8 request.FILES:      包含所有上传文件的类字典对象;FILES中的每一个Key都是<input type="file" name="" />标签中
     9                  name属性的值,FILES中的每一个value同时也是一个标准的python字典对象,包含下面三个Keys:
    10                  filename:      上传文件名,用字符串表示
    11                  content_type:   上传文件的Content Type
    12                  content:       上传文件的原始内容
    13 
    14 request.user:      是一个django.contrib.auth.models.User对象,代表当前登陆的用户。如果访问用户当前
    15                  没有登陆,user将被初始化为django.contrib.auth.models.AnonymousUser的实例。你
    16                  可以通过user的is_authenticated()方法来辨别用户是否登陆:
    17                  if req.user.is_authenticated();只有激活Django中的AuthenticationMiddleware
    18                  时该属性才可用
    19 
    20 request.COOKIES    包含所有cookies的标准Python字典对象;keys和values都是字符串。
    21 request.session     唯一可读写的属性,代表当前会话的字典对象;自己有激活Django中的session支持时该属性才可用

    属性实例 - 上传文件:

     1 def upload(request):
     2     """
     3     保存上传文件前,数据需要存放在某个位置。默认当上传文件小于2.5M时,django会将上传文件的全部内容读进内存。从内存读取一次,写磁盘一次。
     4     但当上传文件很大时,django会把上传文件写到临时文件中,然后存放到系统临时文件夹中。
     5     :param request: 
     6     :return: 
     7     """
     8     if request.method == "POST":
     9         # 从请求的FILES中获取上传文件的文件名,file-name为页面上type=files类型input的name属性值
    10         filename = request.FILES["file-name"].name
    11         # 在项目目录下新建一个文件
    12         with open(filename, "wb") as f:
    13             # 从上传的文件对象中一点一点读
    14             for chunk in request.FILES["file-name"].chunks():
    15                 # 写入本地文件
    16                 f.write(chunk)
    17         return HttpResponse("上传OK")

    (2)方法

    1 request.GET.get('name', None)            拿到GET请求里name的值(不存在就为None)
    2 request.POST.get('username', None)       拿到GET请求里username的值(不存在就为None)
    3 如果某个键对应有多个值,则不能直接用get取值,需要用getlist,如:request.POST.getlist("hobby")
    4
    5 获取请求路径:
    6 请求url: http://127.0.0.1:8000/index.html/23?a=1
    7 request.path 结果: /index.html/23
    8 request.get_full_path() 结果: /index.html/23?a=1

    3.HttpResponse

    对于HttpRequest请求对象来说,是由django自动创建的,但是,HttpResponse响应对象就必须我们自己创建。每个view请求处理方法必须返回一个响应。HttpResponse类在django.http.HttpResponse

    可以由HttpResponse对象上扩展出多种常用方法,如下所示

    (1)HttpResponse函数

    1 from django.shortcuts import HttpResponse
    2 
    3 
    4 # 最简单的视图函数
    5 def easy_view(request):
    6     # 返回最简单的一个响应(一个字符串): hello world!
    7     return HttpResponse("hello world!")

    补充 - JsonResponse

    JsonResponse是HttpResponse的子类,专门用来生成JSON编码的响应:

    1 from django.http import JsonResponse
    2 
    3 response = JsonResponse({'foo': 'bar'})
    4 print(response.content)
    5 
    6 b'{"foo": "bar"}'

    默认只能传递字典类型,如果要传递非字典类型需要设置一下safe关键字参数:

    response = JsonResponse([1, 2, 3], safe=False)

    (2)render函数

    作用:将特点页面渲染后返回给浏览器

    渲染 -> 给页面中的参数赋值

    render(request, template_name[, context])

    结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象

    参数如下:

    • request: 用于生成响应的请求对象
    • template_name:要使用的模板的完整名称,可选的参数
    • context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它
    • content_type:生成的文档要使用的MIME类型。默认为DEFAULT_CONTENT_TYPE 设置的值
    • status:响应的状态码。默认为200

    render实例:

    1 from django.shortcuts import render
    2 
    3 def my_view(request):
    4     # 视图的代码写在这里
    5     return render(request, 'index.html', {'foo': 'bar'})
    1 def render(request, template_name, context=None, content_type=None, status=None, using=None):
    2     """
    3     Returns a HttpResponse whose content is filled with the result of calling
    4     django.template.loader.render_to_string() with the passed arguments.
    5     """
    6     content = loader.render_to_string(template_name, context, request, using=using)
    7     return HttpResponse(content, content_type, status)
    render源码

    关于render:render方法主要是将从服务器提取的数据,填充到模板中,然后将渲染后的html静态文件返回给浏览器

    (3)redirect函数

    redirect函数主要用于重定向,默认返回一个临时的重定向;传递permanent=True 可以返回一个永久的重定向

    参数可以是:

    • 一个模型:将调用模型的get_absolute_url() 函数
    • 一个视图,可以带有参数:将使用urlresolvers.reverse 来反向解析名称
    • 一个绝对的或相对的URL,将原封不动的作为重定向的位置。

    redirect实例: 

     1 from django.shortcuts import redirect
     2 
     3 # 通过一些对象来重定向(将调用该对象的get_absolute_url方法):
     4 def my_view(request):
     5     ...
     6     object = MyModel.objects.get(...)
     7     return redirect(object)
     8 
     9 # 传递视图的名称和可选参数来重定向(将使用reverse方法进行反向解析出url):
    10 def my_view(request):
    11     ...
    12     return redirect('some-view-name', foo='bar')
    13 
    14 # 传递一个url重定向:
    15 def my_view(request):
    16     ...
    17     return redirect('/some/url/')

    对比render和redirect:

    • render: 只是返回页面内容,但是未发送第二次请求
    • redirect:发送了第二次请求,url更新

    另外:

    临时重定向(响应状态码:302)和永久重定向(响应状态码:301)对普通用户来说是没什么区别的,它主要面向的是搜索引擎的机器人。

    • A页面临时重定向到B页面,那搜索引擎收录的就是A页面
    • A页面永久重定向到B页面,那搜索引擎收录的就是B页面

    (4)实战 - 简单登录的完整示例 

    HTML:

    1 form表单往后端提交数据需要注意三点: 
    2 1. form不是from,所有获取用户输入的标签都应该放在form里面, 并且必须要有name属性
    3 2. action属性控制往哪儿提交,method一般都设置成post
    4 3. 提交按钮必须是type=submit,不能是别的类型
     1 <!-- author: wyb -->
     2 <!DOCTYPE html>
     3 <html lang="en">
     4 <head>
     5     <meta charset="UTF-8">
     6     <!-- 移动端适配 -->
     7     <meta name="viewport" content="width=device-width, initial-scale=1">
     8     <title>登录页面</title>
     9     <!-- 引入normalize.css -->
    10     <link href="https://cdn.bootcss.com/normalize/8.0.0/normalize.css" rel="stylesheet">
    11     <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    12     <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
    13           integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    14 
    15     <style>
    16         body{
    17             background: #eeeeee;
    18         }
    19         #form-box{
    20             margin-top: 199px;
    21         }
    22     </style>
    23 </head>
    24 <body>
    25 
    26 <div class="container-fluid">
    27     <div class="row">
    28         <div class="col-md-4 col-lg-3 col-md-offset-4">
    29             <div id="form-box">
    30                 <h3 class="text-center">请登录</h3>
    31                 <form class="form-horizontal" method="post" action="/baobao/">
    32                     <div class="form-group">
    33                         <label for="inputUsername" class="col-sm-3 control-label">用户名</label>
    34                         <div class="col-sm-9">
    35                             <input name="username" type="text" class="form-control" id="inputUsername" placeholder="输入用户名">
    36                         </div>
    37                     </div>
    38                     <div class="form-group">
    39                         <label for="inputPassword3" class="col-sm-3 control-label">密码</label>
    40                         <div class="col-sm-9">
    41                             <input name="password" type="password" class="form-control" id="inputPassword3" placeholder="输入密码">
    42                         </div>
    43                     </div>
    44                     <div class="form-group">
    45                         <div class="col-sm-offset-3 col-sm-9">
    46                             <div class="checkbox">
    47                                 <label>
    48                                     <input type="checkbox"> 记住我
    49                                 </label>
    50                             </div>
    51                         </div>
    52                     </div>
    53                     <div class="form-group">
    54                         <div class="col-sm-offset-3 col-sm-9">
    55                             <button type="submit" class="btn btn-primary btn-block">登录</button>
    56                         </div>
    57                     </div>
    58                 </form>
    59             </div>
    60         </div>
    61     </div>
    62 </div>
    63 
    64 
    65 <!-- 引入jQuery -->
    66 <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
    67 <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
    68 <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"
    69         integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
    70         crossorigin="anonymous"></script>
    71 
    72 </body>
    73 </html>
    登录页面

    urls:

    1 url(r'^login/', views.login),
    2 url(r'^baobao/', views.baobao),

    views(GET和POST分开写):

     1 def login(request):
     2     return render(request, "login.html")
     3 
     4 
     5 def baobao(request):
     6     # 获取用户提交的数据
     7     # 获取所有POST的数据: print(request.POST)
     8     username = request.POST.get("username", None)
     9     password = request.POST.get("password", None)
    10     print(username, password)
    11     # 做是否登录成功的判断
    12     if username == "wyb" and password == "wyb666":
    13         # 登录成功
    14         return HttpResponse("登录成功!")
    15     return HttpResponse("登录失败!")

    views(GET和POST一起写):

     1 # 先把form表单的action属性去掉(不写就是往当前路由提交)
     2 
     3 # 然后修改login函数如下:
     4 def login(request):
     5     if request.method == "GET":
     6         # 如果是GET请求:
     7         return render(request, "login.html")
     8     else:
     9         # 如果是POST请求:
    10         username = request.POST.get("username", None)
    11         password = request.POST.get("password", None)
    12         print(username, password)
    13         # 做是否登录成功的判断
    14         if username == "wyb" and password == "wyb666":
    15             # 登录成功
    16             return HttpResponse("登录成功!")
    17         return HttpResponse("登录失败!")

    views优化版:

     1 # render和redirect实例
     2 # 首先在login.html中的form表单结束之前插入一个p标签(内容: {{ error}})
     3 # 然后修改login函数代码如下:
     4 def login(request):
     5     error_msg = ""
     6     if request.method == "POST":
     7         # 如果是POST请求:
     8         username = request.POST.get("username", None)
     9         password = request.POST.get("password", None)
    10         print(username, password)
    11         # 做是否登录成功的判断
    12         if username == "wyb" and password == "wyb666":
    13             # 登录成功
    14             # return HttpResponse("登录成功!")
    15             # 回复一个特殊的响应(重定向) 这个响应让用户的浏览器自动请求指定的url
    16             return redirect("http://www.luffycity.com")
    17         else:
    18             # 登录失败
    19             error_msg = "用户名或密码错误"
    20     # 不是POST走这下面:
    21     return render(request, "login.html", {"error": error_msg})

    (5)CBV和FBV

    之前写过的都是基于函数的view,就叫FBV。还可以把view写成基于类的。

    就拿上面写的登陆为例:

    FBV版:

     1 # FBV版登陆
     2 def login(request):
     3     error_msg = ""
     4     if request.method == "POST":
     5         username = request.POST.get("username", None)
     6         password = request.POST.get("password", None) 7 
     7         if username == "wyb" and password == "safsfwefvs":
     8             return HttpResponse("登录成功!")
     9         else:
    10             error_msg = "登陆失败: 用户名或密码错误"
    11     return render(request, "user/login.html", {"error": error_msg})

    CBV版:

     1 # CBV版登陆:
     2 from django.views import View
     3 
     4 class Login(View):
     5     def get(self, request):
     6         return render(request, "user/login.html")
     7 
     8     def post(self, request):
     9         username = request.POST.get("username", None)
    10         password = request.POST.get("password", None)
    11         if username == "wyb" and password == "sdfsadf":
    12             return HttpResponse("登录成功!")
    13         else:
    14             error_msg = "登陆失败: 用户名或密码错误"
    15             return HttpResponse(error_msg)

    注意当使用CBV时,urls.py中也做对应的修改:

    # urls.py中
    url(r'^login/$', views.Login.as_view()),

    四、django ORM

    对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。

    简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。

    ORM在业务逻辑层和数据库层之间充当了桥梁的作用

    ORM详细内容:http://www.cnblogs.com/liwenzhou/articles/8276526.html

    1.ORM介绍

    (1)django中的数据库操作

    到目前为止,当我们的程序涉及到数据库相关操作时,我们一般会这样做:

    • 自己创建数据库,设计表结构和字段
    • 使用 pymysql连接数据库,并编写数据访问层代码
    • 业务逻辑层去调用数据访问层执行数据库操作

    django为使用一种新的方式,即:关系对象映射(Object Relational Mapping,简称ORM)来完成上述的数据访问层

    • PHP:activerecord
    • Java:Hibernate 
    • C#:Entity Framework

    django中遵循 Code Frist 的原则,即:根据代码中定义的类来自动生成数据库表

    (2)ORM优缺点

    优点:

    • 简单,不用自己写SQL语句
    • 开发效率高

    缺点:

    • 需要记忆特殊的语法
    • 相对于大神的SQL语句,执行效率有差距


    (3)ORM补充说明

    ORM对应关系:

    1 类 --->   数据表
    2 对象 ---> 数据行
    3 属性 ---> 字段

    实例如下:

    ORM能做的事:

    • 操作数据表 --> 创建表/删除表/修改表  --> 操作models.py里面的类
    • 操作数据行 --> 数据的增删改查

    另外注意ORM不能创建数据库,需要自己动手创建数据库

    2.django中ORM基础配置

    (1)创建数据库
    create database 数据库名;

    (2)设置数据库配置

    在Django项目中的settings.py中设置连接数据库的相关配置(告诉Django连接哪一个数据库)

     1 # Database
     2 # https://docs.djangoproject.com/en/1.11/ref/settings/#databases
     3 
     4 # 默认使用sqlite数据库
     5 # DATABASES = {
     6 #     'default': {
     7 #         'ENGINE': 'django.db.backends.sqlite3',
     8 #         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
     9 #     }
    10 # }
    11 
    12 # 数据库相关配置:
    13 DATABASES = {
    14     'default': {
    15         'ENGINE': 'django.db.backends.mysql',  # 连接的数据库类型
    16         'HOST': '127.0.0.1',  # 连接数据库的地址
    17         'PORT': 3306,  # 端口
    18         'NAME': "django",  # 数据库名称
    19         'USER': 'root',  # 用户
    20         'PASSWORD': 'root'  # 密码
    21     }
    22 }

    (3)__init__.py添加内容

    告诉Django用pymysql代替默认的MySQLDB 连接MySQL数据库
    在项目/__init__.py文件中,写下面两句:

    1 import pymysql
    2 # 告诉Django用pymysql来代替默认的MySQLdb
    3 pymysql.install_as_MySQLdb()

    3.django中ORM使用步骤

    (1)定义类

    在app下面的models.py文件中定义一个类,这个类必须继承models.Model

    1 # ORM相关的只能写在这个文件里,写到别的文件里django找不到
    2 from django.db import models
    3 
    4 
    5 class UserInfo(models.Model):
    6     id = models.AutoField(primary_key=True)     # 创建一个自增的主键字段
    7     # 用户名及密码
    8     username = models.CharField(null=False, max_length=20)
    9     password = models.CharField(null=False, max_length=64)

    (2)执行以下命令

    • python3 manage.py makemigrations -> 监视models文件的改变,将改变记录下来到APP的migrations文件夹下
    • python3 manage.py migrate -> 把上述改动翻译成SQL语句去数据库执行

    4.ORM字段

    (1)Django ORM常用字段

    • AutoField -->  int自增列 必须填入参数primary_key=True
    • CharField -->  字符类型 varchar(xx) 必须提供max_length参数
    • TextField  -->  文本类型
    • ForeignKey --> 外键
    • ManyToManyField --> 多对多关联
    • DateField  -->  日期字段  YYYY-MM-DD 相当于python的datetime.date()实例
    • DateTimeField  -->  日期时间字段 YYYY-MM-DD HH:MM 相当于python的datetime.datetime()实例
    • IntegerField  -->  整数类型
      1 AutoField(Field)
      2         - int自增列,必须填入参数 primary_key=True
      3 
      4     BigAutoField(AutoField)
      5         - bigint自增列,必须填入参数 primary_key=True
      6 
      7         注:当model中如果没有自增列,则自动会创建一个列名为id的列
      8         from django.db import models
      9 
     10         class UserInfo(models.Model):
     11             # 自动创建一个列名为id的且为自增的整数列
     12             username = models.CharField(max_length=32)
     13 
     14         class Group(models.Model):
     15             # 自定义自增列
     16             nid = models.AutoField(primary_key=True)
     17             name = models.CharField(max_length=32)
     18 
     19     SmallIntegerField(IntegerField):
     20         - 小整数 -32768 ~ 32767
     21 
     22     PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
     23         - 正小整数 0 ~ 32767
     24     IntegerField(Field)
     25         - 整数列(有符号的) -2147483648 ~ 2147483647
     26 
     27     PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
     28         - 正整数 0 ~ 2147483647
     29 
     30     BigIntegerField(IntegerField):
     31         - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
     32 
     33     BooleanField(Field)
     34         - 布尔值类型
     35 
     36     NullBooleanField(Field):
     37         - 可以为空的布尔值
     38 
     39     CharField(Field)
     40         - 字符类型
     41         - 必须提供max_length参数, max_length表示字符长度
     42 
     43     TextField(Field)
     44         - 文本类型
     45 
     46     EmailField(CharField):
     47         - 字符串类型,Django Admin以及ModelForm中提供验证机制
     48 
     49     IPAddressField(Field)
     50         - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
     51 
     52     GenericIPAddressField(Field)
     53         - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
     54         - 参数:
     55             protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
     56             unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"
     57 
     58     URLField(CharField)
     59         - 字符串类型,Django Admin以及ModelForm中提供验证 URL
     60 
     61     SlugField(CharField)
     62         - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
     63 
     64     CommaSeparatedIntegerField(CharField)
     65         - 字符串类型,格式必须为逗号分割的数字
     66 
     67     UUIDField(Field)
     68         - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
     69 
     70     FilePathField(Field)
     71         - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
     72         - 参数:
     73                 path,                      文件夹路径
     74                 match=None,                正则匹配
     75                 recursive=False,           递归下面的文件夹
     76                 allow_files=True,          允许文件
     77                 allow_folders=False,       允许文件夹
     78 
     79     FileField(Field)
     80         - 字符串,路径保存在数据库,文件上传到指定目录
     81         - 参数:
     82             upload_to = ""      上传文件的保存路径
     83             storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
     84 
     85     ImageField(FileField)
     86         - 字符串,路径保存在数据库,文件上传到指定目录
     87         - 参数:
     88             upload_to = ""      上传文件的保存路径
     89             storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
     90             width_field=None,   上传图片的高度保存的数据库字段名(字符串)
     91             height_field=None   上传图片的宽度保存的数据库字段名(字符串)
     92 
     93     DateTimeField(DateField)
     94         - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
     95 
     96     DateField(DateTimeCheckMixin, Field)
     97         - 日期格式      YYYY-MM-DD
     98 
     99     TimeField(DateTimeCheckMixin, Field)
    100         - 时间格式      HH:MM[:ss[.uuuuuu]]
    101 
    102     DurationField(Field)
    103         - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
    104 
    105     FloatField(Field)
    106         - 浮点型
    107 
    108     DecimalField(Field)
    109         - 10进制小数
    110         - 参数:
    111             max_digits,小数总长度
    112             decimal_places,小数位长度
    113 
    114     BinaryField(Field)
    115         - 二进制类型
    116 
    117 字段合集
    全部字段

    (2) 自定义char字段

     1 class FixedCharField(models.Field):
     2     """
     3     自定义的char类型的字段类
     4     """
     5     def __init__(self, max_length, *args, **kwargs):
     6         self.max_length = max_length
     7         super(FixedCharField, self).__init__(max_length=max_length, *args, **kwargs)
     8 
     9     def db_type(self, connection):
    10         """
    11         限定生成数据库表的字段类型为char,长度为max_length指定的值
    12         """
    13         return 'char(%s)' % self.max_length


    (3)常用的字段参数

    • null 表示某个字段可以为空
    • default 为字段设置默认值
    • unique 表示该字段在此表中唯一
    • db_index 为此字段设置索引


    DateField和DateTimeField才有的参数:

    • auto_now_add=True --> 创建数据的时候自动把当前时间赋值
    • auto_add=True --> 每次更新数据的时候更新当前时间
    • 注意: 上述两个不能同时设置!!!

    (4)关系字段

    ForeignKey:

    外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 '一对多'中'多'的一方。

    字段参数如下:

    • to:设置要关联的表
    • to_field:设置要关联的表的字段
    • related_name:反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'
    • related_query_name:反向查询操作时,使用的连接前缀,用于替换表名
    • on_delete:删除关联表中的数据时,当前表与其关联的行的变化
    • db_constraint:是否在数据库中创建外键约束,默认为True,赋值为False,表示为软关联

     1 models.CASCADE:
     2 删除关联数据,与之关联也删除
     3 
     4 models.DO_NOTHING:
     5 删除关联数据,引发错误IntegrityError
     6 
     7 models.PROTECT:
     8 删除关联数据,引发错误ProtectedError
     9 
    10 models.SET_NULL:
    11 删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
    12 
    13 models.SET_DEFAULT:
    14 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
    15  
    16 models.SET:
    17 删除关联数据,
    18 a. 与之关联的值设置为指定值,设置:models.SET(值)
    19 b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
    View Code

    OneToOneField:
    一对一字段  通常一对一字段用来扩展已有字段。

    字段参数:

    • to: 设置要关联的表
    • to_field: 设置要关联的字段
    • on_delete: 同ForeignKey字段

    示例:

     1 # 一对一的关联关系多用在当一张表的不同字段查询频次差距过大的情况下,将本可以存储在一张表的字段拆开放置在两张表中,
     2 # 然后将两张表建立一对一的关联关系。
     3 
     4 class Author(models.Model):
     5 name = models.CharField(max_length=32)
     6 info = models.OneToOneField(to='AuthorInfo')
     7 
     8 class AuthorInfo(models.Model):
     9 phone = models.CharField(max_length=11)
    10 email = models.EmailField()

    ManyToManyField:
    用于表示多对多的关联关系。在数据库中通过第三张表来建立关联关系。

    字段参数:

    • to: 设置要关联的表
    • related_name: 同ForeignKey字段
    • related_query_name: 同ForeignKey字段
    • symmetrical: 仅用于多对多自关联时,指定内部是否创建反向操作的字段。默认为True
    • through:手动创建第三张表来管理多对多关系,此时需通过through来指定第三张表的表名
    • through_fields:设置关联的字段
    • db_table:默认创建第三张表时,数据库中表的名称

    实例:

    1 class Person(models.Model):
    2     name = models.CharField(max_length=16)
    3     friends = models.ManyToManyField("self")
    4     # 此时,person对象就没有person_set属性。
    5 
    6 class Person(models.Model):
    7     name = models.CharField(max_length=16)
    8     friends = models.ManyToManyField("self", symmetrical=False)
    9     # 此时,person对象现在就可以使用person_set属性进行反向查询。
     1 # 方式一:自行创建第三张表
     2 class Book(models.Model):
     3     title = models.CharField(max_length=32, verbose_name="书名")
     4 
     5 class Author(models.Model):
     6     name = models.CharField(max_length=32, verbose_name="作者姓名")
     7 
     8 
     9 # 自己创建第三张表,分别通过外键关联书和作者
    10 class Author2Book(models.Model):
    11     author = models.ForeignKey(to="Author")
    12     book = models.ForeignKey(to="Book")
    13 
    14     class Meta:
    15         unique_together = ("author", "book")
    16 
    17 
    18 # 方式二:通过ManyToManyField自动创建第三张表
    19 class Book(models.Model):
    20     title = models.CharField(max_length=32, verbose_name="书名")
    21 
    22 # 通过ORM自带的ManyToManyField自动创建第三张表
    23 class Author(models.Model):
    24     name = models.CharField(max_length=32, verbose_name="作者姓名")
    25     books = models.ManyToManyField(to="Book", related_name="authors")
    26 
    27 
    28 # 方式三:设置ManyTomanyField并指定自行创建的第三张表
    29 class Book(models.Model):
    30     title = models.CharField(max_length=32, verbose_name="书名")
    31 
    32 # 自己创建第三张表,并通过ManyToManyField指定关联
    33 class Author(models.Model):
    34     name = models.CharField(max_length=32, verbose_name="作者姓名")
    35     books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book"))
    36     # through_fields接受一个2元组('field1','field2'):
    37     # 其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。
    38 
    39 class Author2Book(models.Model):
    40     author = models.ForeignKey(to="Author")
    41     book = models.ForeignKey(to="Book")
    42 
    43     class Meta:
    44         unique_together = ("author", "book")
    45 
    46 # 注意: 当我们需要在第三张关系表中存储额外的字段时,就要使用第三种方式
    47 但是当我们使用第三种方式创建多对多关联关系时,就无法使用set、add、remove、clear方法来管理多对多的关系了,
    48 需要通过第三张表的model来管理多对多关系
    多对多关联关系

    (5)元信息

    ORM对应的类里面包含另一个Meta类,而Meta类封装了一些数据库的信息。主要字段如下:

    • db_table:ORM在数据库中的表名默认是 app_类名,可以通过db_table可以重写表名
    • index_together:联合索引
    • unique_together:联合唯一索引
    • ordering:指定默认按什么字段排序
    • 只有设置了该属性,我们查询到的结果才可以被reverse()

    例如设置重写表名:

    1 class Meta:
    2     db_table = "xxx"

    5.ORM操作

    (1)操作数据表  -->  创建表/删除表/修改表

    使用ORM操作数据表实际上就是按上述ORM使用步骤来操作models.py里面的类(把上面第一步替换成如下步骤):

    • 创建表:创建对应的类(定义类)
    • 删除表:删除对应的类(删除类)
    • 修改表:修改对应的类(修改类)

    (2)操作数据行  -->  数据的增删改查

    • ORM查询数据  models.UserInfo.objects.all()、
    • ORM添加数据  models.UserInfo.objects.create(name="张三")
    • ORM删除数据  models.UserInfo.objects.get(id=user_id).delete()
    • ORM更新数据  models.UserInfo.objects.get(id=user_id).xxx = xxx -> save()

    ORM操作数据详细讲解看这里:https://www.cnblogs.com/liwenzhou/p/8660826.html

    详细如下:

     1 # 增:
     2 #
     3 # models.Tb.objects.create(c1='xx', c2='oo')  增加一条数据,可以接受字典类型数据 **kwargs
     4 # obj = models.Tb(c1='xx', c2='oo')
     5 # obj.save()
     6 
     7 
     8 #
     9 #
    10 # models.Tb.objects.all()  # 获取全部
    11 # models.Tb.objects.filter(name='seven')  # 获取指定条件的数据
    12 
    13 #
    14 #
    15 # models.Tb1.objects.filter(name='seven').delete()  # 删除指定条件的数据
    16 
    17 #
    18 # models.Tb1.objects.filter(name='seven').update(gender='0')  # 将指定条件的数据更新,均支持 **kwargs
    19 # obj = models.Tb1.objects.get(id=1)
    20 # obj.c1 = '111'
    21 # obj.save()                                                 # 修改单条数据
    22         
    23                     
    24                     

    6.ORM表关系

    (1)三种表关系

    ORM表关系有如下三种:

    • 一对多:models.ForeignKey(其他表)
    • 多对多:models.ManyToManyField(其他表)
    • 一对一:models.OneToOneField(其他表)

    实例如下:

    一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择)
    例如:创建用户信息时候,需要选择一个用户类型【普通用户】【会员用户】【超级会员用户】等
    
    多对多:在某表中创建一行数据是,有一个可以多选的下拉框
    例如:创建用户信息,需要为用户指定多个爱好 但同时一个爱好可以被多个用户选择
    
    一对一: 比如公司对应的董事长只能有一个

    (2)详细使用

      1 ForeignKey(ForeignObject) # ForeignObject(RelatedField)
      2         to,                         # 要进行关联的表名
      3         to_field=None,              # 要关联的表中的字段名称
      4         on_delete=None,             # 当删除关联表中的数据时,当前表与其关联的行的行为
      5                                         - models.CASCADE,删除关联数据,与之关联也删除
      6                                         - models.DO_NOTHING,删除关联数据,引发错误IntegrityError
      7                                         - models.PROTECT,删除关联数据,引发错误ProtectedError
      8                                         - models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
      9                                         - models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
     10                                         - models.SET,删除关联数据,
     11                                                       a. 与之关联的值设置为指定值,设置:models.SET(值)
     12                                                       b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
     13 
     14                                                         def func():
     15                                                             return 10
     16 
     17                                                         class MyModel(models.Model):
     18                                                             user = models.ForeignKey(
     19                                                                 to="User",
     20                                                                 to_field="id"
     21                                                                 on_delete=models.SET(func),)
     22         related_name=None,          # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
     23         related_query_name=None,    # 反向操作时,使用的连接前缀,用于替换【表名】     如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
     24         limit_choices_to=None,      # 在Admin或ModelForm中显示关联数据时,提供的条件:
     25                                     # 如:
     26                                             - limit_choices_to={'nid__gt': 5}
     27                                             - limit_choices_to=lambda : {'nid__gt': 5}
     28 
     29                                             from django.db.models import Q
     30                                             - limit_choices_to=Q(nid__gt=10)
     31                                             - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
     32                                             - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
     33         db_constraint=True          # 是否在数据库中创建外键约束
     34         parent_link=False           # 在Admin中是否显示关联数据
     35 
     36 
     37     OneToOneField(ForeignKey)
     38         to,                         # 要进行关联的表名
     39         to_field=None               # 要关联的表中的字段名称
     40         on_delete=None,             # 当删除关联表中的数据时,当前表与其关联的行的行为
     41 
     42                                     ###### 对于一对一 ######
     43                                     # 1. 一对一其实就是 一对多 + 唯一索引
     44                                     # 2.当两个类之间有继承关系时,默认会创建一个一对一字段
     45                                     # 如下会在A表中额外增加一个c_ptr_id列且唯一:
     46                                             class C(models.Model):
     47                                                 nid = models.AutoField(primary_key=True)
     48                                                 part = models.CharField(max_length=12)
     49 
     50                                             class A(C):
     51                                                 id = models.AutoField(primary_key=True)
     52                                                 code = models.CharField(max_length=1)
     53 
     54     ManyToManyField(RelatedField)
     55         to,                         # 要进行关联的表名
     56         related_name=None,          # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
     57         related_query_name=None,    # 反向操作时,使用的连接前缀,用于替换【表名】     如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
     58         limit_choices_to=None,      # 在Admin或ModelForm中显示关联数据时,提供的条件:
     59                                     # 如:
     60                                             - limit_choices_to={'nid__gt': 5}
     61                                             - limit_choices_to=lambda : {'nid__gt': 5}
     62 
     63                                             from django.db.models import Q
     64                                             - limit_choices_to=Q(nid__gt=10)
     65                                             - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
     66                                             - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
     67         symmetrical=None,           # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段
     68                                     # 做如下操作时,不同的symmetrical会有不同的可选字段
     69                                         models.BB.objects.filter(...)
     70 
     71                                         # 可选字段有:code, id, m1
     72                                             class BB(models.Model):
     73 
     74                                             code = models.CharField(max_length=12)
     75                                             m1 = models.ManyToManyField('self',symmetrical=True)
     76 
     77                                         # 可选字段有: bb, code, id, m1
     78                                             class BB(models.Model):
     79 
     80                                             code = models.CharField(max_length=12)
     81                                             m1 = models.ManyToManyField('self',symmetrical=False)
     82 
     83         through=None,               # 自定义第三张表时,使用字段用于指定关系表
     84         through_fields=None,        # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表
     85                                         from django.db import models
     86 
     87                                         class Person(models.Model):
     88                                             name = models.CharField(max_length=50)
     89 
     90                                         class Group(models.Model):
     91                                             name = models.CharField(max_length=128)
     92                                             members = models.ManyToManyField(
     93                                                 Person,
     94                                                 through='Membership',
     95                                                 through_fields=('group', 'person'),
     96                                             )
     97 
     98                                         class Membership(models.Model):
     99                                             group = models.ForeignKey(Group, on_delete=models.CASCADE)
    100                                             person = models.ForeignKey(Person, on_delete=models.CASCADE)
    101                                             inviter = models.ForeignKey(
    102                                                 Person,
    103                                                 on_delete=models.CASCADE,
    104                                                 related_name="membership_invites",
    105                                             )
    106                                             invite_reason = models.CharField(max_length=64)
    107         db_constraint=True,         # 是否在数据库中创建外键约束
    108         db_table=None,              # 默认创建第三张表时,数据库中表的名称
    View Code

    (3)ORM多对多关联查询原理

    多对多其实本质上要通过一张中间表来实现查找,中间表就如下图中的author2book表:

    (4)使用实例 - 图书管理系统

    表结构说明:

     1 图书管理系统:
     2 主要是练习增删改查操作以及表与表之间的关联关系
     3 
     4 图书管理系统的三种角色: 出版社、书、作者,这三种关系对应三张表
     5 
     6 表之间的关系
     7 一本书 只能 有一个出版社
     8 一本书 能有 多个作者
     9 一个作者 能写 多本书
    10 
    11 出版社和书: 一对多 --> 外键(放在多的一端)
    12 书和作者: 多对多 --> 用第三张表做关联

    ORM类如下:

     1 from django.db import models
     2 
     3 # 出版社
     4 class Publisher(models.Model):
     5     id = models.AutoField(primary_key=True)
     6     # 创建一个唯一的不为空的字段
     7     name = models.CharField(max_length=64, null=False, unique=True)
     8 
     9     def __str__(self):
    10         return "<Publisher Object: {}>".format(self.name)
    11 
    12 
    13 #
    14 class Book(models.Model):
    15     id = models.AutoField(primary_key=True)
    16     # 创建一个唯一的不为空的字段
    17     title = models.CharField(max_length=64, null=False, unique=True)
    18     # 和出版社关联的外键字段  存储到数据中会自动加上_id变成publisher_id
    19     publisher = models.ForeignKey(to="Publisher")
    20 
    21     def __str__(self):
    22         return "<Book Object: {}>".format(self.title)
    23 
    24 
    25 # 作者
    26 class Author(models.Model):
    27     id = models.AutoField(primary_key=True)
    28     name = models.CharField(max_length=32, null=False, unique=True)
    29     book = models.ManyToManyField(to="Book")    # 告诉ORM Author和Book是多对多关系 自动生成第三张表
    30 
    31     def __str__(self):
    32         return "<Author Object: {}>".format(self.name)

    添加书籍:

    1 def add_book(request):
    2     if request.method == "POST":
    3         new_title = request.POST.get("book_title")
    4         new_publisher_id = request.POST.get("publisher")  # 书籍关联的出版社id
    5         # print(new_title, new_publisher_id)
    6         if new_title:
    7             models.Book.objects.create(title=new_title, publisher_id=new_publisher_id)
    8 
    9     return redirect("/book/book_list/")

    添加作者:

     1 def add_author(request):
     2     if request.method == "POST":
     3         # 注意post提交如果获取多选要用getlist:
     4         author_name = request.POST.get("author_name")
     5         books = request.POST.getlist("books")           # book的一系列id
     6         # print(author_name, books)
     7         # 创建作者并把作者和书籍建立关系
     8         if author_name:
     9             new_author_obj = models.Author.objects.create(name=author_name)
    10             new_author_obj.book.set(books)
    11 
    12     return redirect("/book/author_list/")

    编辑书籍:

     1 def edit_book(request):
     2     if request.method == "POST":
     3         # 从提交的数据里面取要编辑的书的以下内容: id 书名 书关联的出版社
     4         edit_id = request.POST.get("id")
     5         new_title = request.POST.get("book_title")
     6         new_publisher_id = request.POST.get("publisher")
     7         # 书名为空或书关联的出版社不存在时重定向
     8         if not new_title or not models.Publisher.objects.filter(id=new_publisher_id):
     9             return redirect("/book/book_list/")
    10         # 更新
    11         edit_book_obj = models.Book.objects.filter(id=edit_id)[0]
    12         if edit_book_obj:
    13             edit_book_obj.title = new_title  # 更新书名
    14             edit_book_obj.publisher_id = new_publisher_id  # 更新书籍关联的出版社
    15             # 将修改提交到数据库
    16             edit_book_obj.save()
    17         else:
    18             return HttpResponse("书籍不存在!")
    19         # 返回书籍列表页面,查看是否编辑成功
    20         return redirect("/book/book_list/")

    编辑作者:

     1 def edit_author(request):
     2     if request.method == "POST":
     3         # 从提交的数据里面取要编辑的作者的以下内容: id 作者名 作者的一系列作品id
     4         edit_author_id = request.POST.get("author_id")
     5         new_author_name = request.POST.get("author_name")
     6         new_books = request.POST.getlist("books")
     7         print(edit_author_id, new_author_name, new_books)
     8         # 作者名为空时重定向
     9         if not new_author_name:
    10             return redirect("/book/author_list/")
    11         # 更新
    12         edit_author_obj = models.Author.objects.filter(id=edit_author_id)[0]
    13         if edit_author_obj:
    14             edit_author_obj.name = new_author_name        # 更新作者名
    15             edit_author_obj.book.set(new_books)           # 更新作者关联的作品
    16             # 将修改提交到数据库
    17             edit_author_obj.save()
    18         else:
    19             return HttpResponse("作者不存在!")
    20         # 返回书籍列表页面,查看是否编辑成功
    21         return redirect("/book/author_list/")

    注意:以上添加功能和编辑功能没有get请求,实际上在这里后端省去了对get请求的处理(也就是添加和编辑没有新页面出现),

    在这里点击添加或编辑后会在当前页面(书籍列表页或作者列表页)出现一个弹框,点击弹框的提交键即可向后端提交数据,

    关于弹框的实现,可以自定义弹框,也可以使用Bootstrap的弹框样式,我个人是直接使用Bootstrap的弹框

    7.其他

    数据已有数据修改表结构问题:

    五、模板

    模板详细内容:http://www.cnblogs.com/liwenzhou/p/7931828.html

    1.常用语法

    只需要记两种特殊符号:{{ }}和 {% %}
    变量相关的用{{}},逻辑相关的用{%%}

    (1)变量相关

     1 {{ 变量名 }} -> 变量名由字母数字和下划线组成
     2 点(.)在模板语言中有特殊的含义,用来获取对象的相应属性值
     3 
     4 {# 这是注释 #}
     5 
     6 {# 取s中的第一个参数 #}
     7 {{ s.0 }}
     8 {# 取字典中key的值 #}
     9 {{ d.name }}
    10 {# 取对象的name属性 #}
    11 {{ person_list.0.name }}
    12 {# .操作只能调用不带参数的方法 #}
    13 {{ person_list.0.dream }}

    (2)tags

     1 # for相关
     2 for循环:
     3     <ul>
     4     {% for user in user_list %}
     5         <li>{{ user.name }}</li>
     6     {% endfor %}
     7     </ul>
     8 
     9 for循环中可用的参数:
    10     forloop.counter    当前循环的索引值(从1开始)
    11     forloop.counter0    当前循环的索引值(从0开始)
    12     forloop.revcounter    当前循环的倒序索引值(从1开始)
    13     forloop.revcounter0    当前循环的倒序索引值(从0开始)
    14     forloop.first    当前循环是不是第一次循环(布尔值)
    15     forloop.last    当前循环是不是最后一次循环(布尔值)
    16     forloop.parentloop    本层循环的外层循环
    17 
    18 for ... empty:
    19     <ul>
    20     {% for user in user_list %}
    21         <li>{{ user.name }}</li>
    22     {% empty %}
    23         <li>空空如也</li>
    24     {% endfor %}
    25     </ul>
    26 
    27 
    28 # if相关
    29 if和else:
    30     {% if 判断条件 %}
    31         xxx
    32     {% else %}
    33         xxx
    34     {% endif %}
    35 
    36 
    37 if,elif和else:
    38     {% if 判断条件 %}
    39         xxx
    40     {% elif 判断条件 %}
    41         xxx
    42     {% else %}
    43         xxx
    44     {% endif %}
    45 
    46 另外if语句支持 andor、==、>、<、!=、<=、>=、innot inisis not判断
    47 if ... in 判断:
    48     {% if xxx in xxx %}
    49         ...
    50     {% endif %}
    51 
    52 
    53 with: 定义一个中间变量
    54 {% with total=business.employees.count %}
    55     {{ total }} employee{{ total|pluralize }}
    56 {% endwith %}

    (3)filter

    filter主要是针对模板中的变量做一些额外的操作,语法: {{ value|filter_name:参数 }}

    常见的如下:

    • default: {{ value|default: "nothing"}}  赋予默认值
    • length: {{ value|length }}  返回value的长度
    • filesizeformat:将值格式化为一个可读的文件尺寸 {{ value|filesizeformat }},如果value 是 123456789,输出将会是 117.7 MB
    • slice - 切片:{{value|slice:"2:-1"}}
    • date - 格式化:{{ value|date:"Y-m-d H:i:s"}}
    • truncatechars - 截断字符串:{{ value|truncatechars:9}},如果字符串字符多于指定的字符数量,将会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾

    safe - 关闭自动转义

    Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全(防止XSS攻击)。但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。

    比如:

    value = "<a href='#'>点我</a>"

    1 {{ value|safe}}

    自定义filter

    自定义过滤器只是带有一个或两个参数的Python函数:

    • 变量(输入)的值 - -不一定是一个字符串
    • 参数的值 - 这可以有一个默认值,或完全省略

    例如,在过滤器{{var|foo:“bar”}}中,过滤器foo将传递变量var和参数“bar”

    自定义filter代码文件(py文件)要放在如下位置:

    1 app/
    2     __init__.py
    3     models.py
    4     templatetags/  # 在app下面新建一个package package
    5         __init__.py
    6         app_filters.py  # 建一个存放自定义filter的文件
    7     views.py

    编写自定义filter:

     1 from django import template
     2 register = template.Library()
     3 
     4 # 带参数的filter
     5 @register.filter(name="cut")
     6 def cut(value, arg):
     7     return value.replace(arg, "")
     8 
     9 # 不带参数的filter
    10 @register.filter(name="addSB")
    11 def add_sb(value):
    12     return "{} SB".format(value)

    上述代码注释中的参数是指filter中的参数,而真正函数中的第一个参数(函数参数)永远传入的是value的值(|前面那个值)

    使用自定义filter:

    1 {# 先导入我们自定义filter那个文件 #}
    2 {% load app_filters %}
    3 
    4 {# 使用我们自定义的filter #}
    5 {{ somevariable|cut:"0" }}
    6 {{ d.name|addSB }}

    2.母版和继承

    (1)为什么要有母版和继承

    把多个页面公用的部分提取出来,放在一个 母版 里面。其他的页面只需要 继承 母版就可以了。可以简化代码,写更少的代码

    (2)具体使用步骤

    • 把公用的HTML部分(head、css、js、固定的HTML)提取出来,放到base.html文件中
    • 在base.html中,通过定义block,把每个页面不同的部分区分出来(大致划分:页面的特殊CSS、页面的主体内容、页面的特殊Js、页面特殊组件)
    • 在具体的页面中,先继承母版
    • 然后block名去指定替换母版中相应的位置

    base.html:

    1 <!-- 页面主体 -->
    2 <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
    3     {% block page-main %}
    4     
    5     {% endblock %}
    6 </div>

    具体的页面中:

    1 {# 继承母版 #}
    2 {% extends "base.html" %}
    3 
    4 {# 把自己页面的内容塞到母版中相应的位置 #}
    5 
    6 {% block page-main %}
    7     <h1>这是具体页面</h1>
    8 {% endblock %}

    (3)使用的注意事项

    • {% extends 'base.html' %} --> 母版文件:base.html要加引号
    • {% extends 'base.html' %}必须放在子页面的第一行!!!
    • 可以在base.html中定义很多block,通常我们会额外定义page-css和page-js两个块
    • view.py相应的函数中返回的是对应的子页面文件 而不是base.html!

    3.组件及静态文件

    (1)什么是模板中的组件

    可以将常用的页面内容如导航条,页尾信息等组件保存在单独的文件中,然后在需要使用的地方按如下语法导入即可:

    1 {% include 'navbar.html' %}

    (2)静态文件导入问题

    静态文件:图片、CSS、js

    前面我们的静态文件的路径都是写死的,如过将static文件夹的名字改为static333,在代码中我们还要修改多处,这样十分不方便,不过django中提供了方法,可以让HTML中的静态文件路径动态跟随文件夹的名字变化而变化,从而实现动态导入

    引入CSS:

    1 {% load static %}
    2 <link href="{% static 'CSS路径' %}" rel="stylesheet">

    引入图片:

    1 {% load static %}
    2 <img src="{% static '图片路径' %}" alt="Hi" />

    引用JS文件:

    1 {% load static %}
    2 <script src="{% static 'js路径' %}"></script>

    上述导入实例:

    另外某个文件多处被用到可以存为一个变量:

    1 {% load static %}
    2 {% static "images/hi.jpg" as myphoto %}
    3 <img src="{{ myphoto }}"></img>

    (3)get_static_prefix

    上面的导入静态文件过程,是自动拼接static目录名和后面的路径,导入我们也可以手动拼接:

    1 {% load static %}
    2 <img src="{% get_static_prefix %}images/hi.jpg" alt="Hi!" />
    3 
    4 或者:
    5 {% load static %}
    6 {% get_static_prefix as STATIC_PREFIX %}
    7 
    8 <img src="{{ STATIC_PREFIX }}images/hi.jpg" alt="Hi!" />
    9 <img src="{{ STATIC_PREFIX }}images/hi2.jpg" alt="Hello!" />

    (4)自定义simpletag

    类似上面的自定义filter不过使用起来更加自由

    自定义simpletag文件(py文件)放在如下位置:

    1 app/
    2     __init__.py
    3     models.py
    4     templatetags/  # 在app下面新建一个package -> 放自定义filter文件和自定义simpletag文件
    5         __init__.py
    6         app_filters.py  # 建一个存放自定义filter的文件
    7         app_simpletag.py  # 建一个存放自定义simpletag的文件
    8     views.py

    编写自定义simpletag:

    1 from django import template
    2 
    3 register = template.Library()
    4 
    5 @register.simple_tag(name="plus")
    6 def plus(a, b, c):
    7     return "{} + {} + {}".format(a, b, c)

    使用自定义simpletag:

    1 {% load app_simpletag.py %}
    2 
    3 {# simple tag #}
    4 {% plus "1" "2" "abc" %}

    (5)inclusion_tag

    上面的自定义simpletag无法返回HTML,要返回HTML就要使用inclusion_tag,使用方法如下:

     1 templatetags/my_inclusion.py:
     2 from django import template
     3 register = template.Library()
     4 
     5 @register.inclusion_tag('result.html')
     6 def show_results(n):
     7     n = 1 if n < 1 else int(n)
     8     data = ["第{}项".format(i) for i in range(1, n+1)]
     9     return {"data": data}
    10 
    11 
    12 templates/result.html:13 <ul>
    14   {% for choice in data %}
    15     <li>{{ choice }}</li>
    16   {% endfor %}
    17 </ul>
    18 
    19 
    20 templates/index.html:
    21 <!DOCTYPE html>
    22 <html lang="en">
    23 <head>
    24   <meta charset="UTF-8">
    25   <meta http-equiv="x-ua-compatible" content="IE=edge">
    26   <meta name="viewport" content="width=device-width, initial-scale=1">
    27   <title>inclusion_tag test</title>
    28 </head>
    29 <body>
    30 
    31 {% load inclusion_tag_test %}
    32 {% show_results 10 %}
    33 
    34 </body>
    35 </html>
  • 相关阅读:
    联想笔记本如何开启笔记本的VT-x虚拟化技术功能
    《Python数据分析》环境搭建之安装Jupyter工具(一)
    SQLite文件查看工具DB Browser for SQLite
    《Python操作SQLite3数据库》快速上手教程
    市面上各类网盘(百度网盘、腾讯微云、亿方云、坚果云等)对比 2016年10月
    Selenium安装失败WebDriverException: Message: 'gechodriver' executable needs to be in PATH
    安装Python环境时遇到的问题
    ORA-12541:TNS没有监听器
    PL/SQL连接Oracle数据库,中文乱码,显示问号
    Selenium 简单的例子
  • 原文地址:https://www.cnblogs.com/wyb666/p/9464983.html
Copyright © 2020-2023  润新知