django 总结

该文章没有任何理论概念的解释,建议读者具备基本web框架知识

1.视图函数

def login(req):
    print(req.method)
    # 判断请求方式 注意都是大写的
    if req.method == "GET":
        # 直接返回页面  这里是使用请求方式来区分同一个地址的 请求的目的
        return render(req, "login.html")
    elif req.method == "POST":
        # 如果是post请求就获取数据
        # print(req.POST) # POST  或GET 获取到的都是一个字典QueryDict类型
        # print(req.POST.get("hobby")) # 每个值都是一个数组类型  直接get得到的是最后一个值
        # print(req.POST.getlist("hobby"))  # 获取所有值 以列表形式


        # 获取用户名和密码:
        user = req.POST.get("user")
        pwd = req.POST.get("pwd")

        # 查询数据库
        pool = ZS_POOL.ConnectPool()
        res = pool.execute_sql("select *from person where name = %s and password = %s",[user,pwd])
        print(res)
        if res[0]:
            return HttpResponse("login success!")
        return HttpResponse("login failed!")

    return HttpResponse()

1.1视图函数的返回值

django要求视图函数必须返回一个HTTPResponse对象,render函数和 redirect 函数其实都是返回了一个特定的HTTPResponse对象

1.2 reder函数

render函数用于渲染一个模板文件,这里的渲染指的是将动态数据填充到页面文件中,

该函数接收一个request请求对象,一个模板文件,一个context,

其中context用于,向模板中传递数据,以字典形式,模板文件中可以使用模板语言从context中取出数据

案例:

def user_list(req):
    if req.method == "GET":
        return render(req,"users.html",context={"users":["jack","rose"]})

1.3 redirect函数

redirect函数用于重定向页面,返回值为HttpResonse,需要说明的是重定向的本质是让客户端重新发起新请求,请求另一个地址

案例:

return redirect("/userlist")

1.4 JsonResponse

JsonResponse继承自HttpResponse,用于向前台返回json格式的响应数据

return JsonResponse({"users":["json","scot"]})



return JsonResponse(["json","scot"])
# 上面报错 因为默认只能序列化字典格式,如果要序列化其他类型 需要价格safe设置为False
return JsonResponse(["json","scot"],safe=False)

2.配置文件

2.1静态文件配置

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/

STATIC_URL = '/static/'  # 静态路由, 路径为根路径+/static时将返回下列路径中的静态文件

# 静态文件搜索路径 ,可以是列表
STATICFILES_DIRS = [
    os.path.join(BASE_DIR,"static")
]

在模板中可以从像反向解析一样从配置文件中拿到这个静态前缀

{% load static %}
{% get_static_prefix %}
<a href="{% get_static_prefix %}upload.html">page</a>

# 注意不要加/ 
<a href="{% get_static_prefix %}/upload.html">page</a>

2.2模板路径配置

image-20190916102755612

补充:pycharm链接数据库

image-20190916103320083

3.路由

3.1 提取路径中的参数

1.* 的url 函数 与2.* re_path 使用方式一致

第一个参数为正则表达式,第二个是要调用的函数名称

re_path下的配置以及参数捕获

先声明:不使用复杂的url配置直接将参数放在?后面也是可以完成所有需求的

那么使用复杂的配置从path中取值的目的是什么呢? 伪静态!

而伪静态是为了SEO优化

  • 无名分组:

    从path中使用re获取值然后当做位置参数传给视图函数

re_path('^book/(\d+)/$', views.book),

#视图函数
def book(request,any_name):
  pass
  • 有名分组:

    从path中使用re获取值然后当做关键字参数传给视图函数

re_path('^book/(?P<id>\d+)/$', views.book),

#视图函数  参数名称必须与url配置中完全相同
def book(request,id):
  pass

3.2 2.* 下的path

2.*版本下默认使用的是path,path其实就是老版本url的有名分组,捕获的参数都会以关键字的形式传给视图函数

  • 用法:
path('gets/<page>/info', views.gets),

# <> 表示分组  括号中的page是参数名称


  • 转换器

    本质就是封装的正则 最后在帮你做了类型转换

path('gets/<int:page>/info', views.gets),
  • 自定义转换器

    当你的规则比较复杂或是要转换为特殊的类型时可以自定义转换器,有以下要求

    • regex 类属性,字符串类型
    • to_python(self, value) 方法,value是由类属性 regex 所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中。
    • to_url(self, value) 方法,和 to_python 相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用。

    案例:

    class FourDigitYearConverter:
        regex = '[0-9]{4}'
        def to_python(self, value):
            return int(value)
        def to_url(self, value):
            return '%04d' % value
    # 该转换器用于匹配4位的整数 并转为int类型
    

    使用:

    # 注册
    register_converter(converters.FourDigitYearConverter, 'yyyy')
    # 使用
    urlpatterns = [
        path('articles/2003/', views.special_case_2003),
        path('articles/<yyyy:year>/', views.year_archive),
    

3.3反向解析

何为反向,正常的请求流程是先走路由,在执行视图函数渲染模板,

当我们在模板或者视图函数中需要获取路由信息时就称之为反向解析

为什么要这样做呢?,目的是为了方便后续修改路由配置,反向解析可以获取请求路径信息,而避免直接写死路径带来的麻烦事

下例方法适用于1.* 中的url函数

首先需要在路由中为这个路由指定一个名称

  • 不带分组
#路由
re_path('books', views.books,name="path1")

#视图函数取值
print(reverse("path1"))

#模板取值
<h1>{% url "path1" %}</h1>
  • 无名分组
#路由
re_path('books/(\d+)', views.books2,name="path2"),

#视图函数取值
print(reverse("path2",args=("100",)))

#模板取值
<h1>{% url "path2" "1" %}</h1>
  • 有名分组
#路由
re_path('books3/(?P<id>\d+)', views.books3,name="path3"),


#视图函数取值
print(reverse("path3", args=("100",)))
# or
print(reverse("path3", kwargs={"id": "100"}))

#模板取值
<h1>{% url "path3" "1" %}</h1>
# or
<h1>{% url "path3" id="1" %}</h1>

另外反向解析本质就是做字符串拼接,所以无论在什么位置只要存在就可以解析

3.4路由分发

当应用程序体积大 模块多时需要拆分多个不同APP,同时urls也不能全都写在主路由配置中

这就需要在主路由配置文件中引入其他app中的路由配置信息

  • 引入案例:

主路由配置文件:

from django.urls import include, path

urlpatterns = [
   # 可以是完整的模块路径
    path('help/', include('apps.help.urls')),
   # 可以是一个已经导入的模块名称 
    path('credit/', include(a_urls)),
]

当请求路径为 http:xxxxxxx:8000/help/test/ 是将请求交给apps.help.urls来进行匹配,

需要强调的是在apps.help.urls中拿到的路径是已经去除前缀的,也就是只有/test/部分

  • 使用include来简化书写:

简化前

urlpatterns = [
    path('<page_slug>-<page_id>/history/', views.history),
    path('<page_slug>-<page_id>/edit/', views.edit),
    path('<page_slug>-<page_id>/discuss/', views.discuss),
    path('<page_slug>-<page_id>/permissions/', views.permissions),
]

简化后:

urlpatterns = [
    path('<page_slug>-<page_id>/', include([
        path('history/', views.history),
        path('edit/', views.edit),
        path('discuss/', views.discuss),
        path('permissions/', views.permissions),
    ])),
]

路由分发中的名称空间

当多个app中出现了相同名称的路由时,反向解析将出现问题

  • 解决方案1

手动给name添加别名

re_path('^books$', views.books,name="appname_path1"),
  • 解决方案2

在include中指定namespace

path("app01/",include("app01.urls",namespace="app01"))

#视图函数取值
print(reverse("app01:path1"))

#模板取值
<h1>{% url "app01:path1" %}</h1>

FBV - CBV

Function Base View 基于函数的视图

Class Base View 基于class的视图:


# 路由
path('student/', views.Student.as_view()),

# 视图
class Student(View):

    def get(self,request):
        print("get")
        return HttpResponse("get request<form action='' method='post'><input type='submit'></form>")

    def post(self, request):
        print("post")
        return HttpResponse("post request")

注意:函数必须是小写的 且接收一个request参数 源码如下:

handler = getattr(self, request.method.lower(), self.http_method_not_allowed)

文件上传

前台:

 <form action="/app1/upload_file" method="post" enctype="multipart/form-data">
{#    <form action="/app1/upload_file" method="post" enctype="application/x-www-form-urlencoded">#}
        <input value="12312" name="info">
        <input type="file" name="file1">
        <input type="file" name="file2">
        <input type="submit">
    </form>

后台:

class FileUpload(View):
    def post(self,request):
        print(request.POST)
        print(request.FILES)
        for name in request.FILES:
            file = request.FILES.get(name)
            with open(file.name,"wb") as f:
                for line in file:
                    f.write(line)
        return JsonResponse("ok",safe=False)

注意:经过测试enctype="multipart/form-data" 或者 enctype="application/x-www-form-urlencoded" 都是可以上传成功的 ,另外除了文件以外的参数也可以通过POST来获取

模板

语法:

重点:模板访问context中的内容

contexnt是模板与视图交互的容器,是一个字典类型

在模板中使用{{ key名称 }} 来从context中取出一个值,并调用__str拿到这个值的字符串表现形式进行渲染

<h1>{{ title }}</h1>

用点来展开这个值的属性,如果要访问的属性是一个函数会自动执行,并拿到函数返回值来渲染

<h1>{{ user.name }}</h1>

过滤器:

这个名字很费解,但是过滤器的本质目的就是对即将渲染的值进行一些简单处理,例如字符串截断,空值判断,长度,单位转换等

语法:

{{obj|filter__name:param}}  要处理变量名字|过滤器名称:另一个参数(如果需要)
#实例:
{{ value|default:"nothing" }}
如果value为空则显示nothing

标签:

语法:

{% for | if | with.... %}

{% endfor | endif endwith%}

实例:

{% if num > 100 or num < 0 %}
    <p>无效</p>
{% elif num > 80 and num < 100 %}
    <p>优秀</p>
{% else %}
    <p>凑活吧</p>
{% endif %}

标签的目的是在模板中使用模板语言来玩一些基础的逻辑处理,例如循环,判断.等

过滤器与标签的本质都是一个函数,我们可以自己来定义标签和过滤器,按照以下方式:

1、在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag.

2、在app中创建templatetags模块(模块名只能是templatetags)

3、创建任意 .py 文件,如:my_tags.py

案例:

from django import template
from django.utils.safestring import mark_safe
 
register = template.Library()   #register的名字是固定的,不可改变
 
 
@register.filter
def filter_multi(v1,v2):
    return  v1 * v2
<br>
@register.simple_tag
def simple_tag_multi(v1,v2):
    return  v1 * v2
<br>
@register.simple_tag
def my_input(id,arg):
    result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
    return mark_safe(result)
  
  
# 最后在模板中使用load来加载自定义标签和过滤器:
{% load my_tags %} 

模板的包含与继承

这两个点对于大型项目而言非常重要,因为很多页面具备相同的地方,前端如果不用框架的话,要是先页面复用不是一件容易的事情,但是放到后台来做就非常简单了;

其目的是为了复用已存在的模板或者是将页面分为不同板块进行解耦合

  • 包含
语法:{% include '模版名称' %}
如:{% include 'adv.html' %}
#本质是直接将被包含的文件内容放到这个位置,字符串替换一个道理
  • 继承

继承则是在父级模板中 本质是提前留一个字符串空间(域,或者叫做范围 有开始 有结束 ),并给这个空间取个名称,然后在子模板中编写内容,替换到这个位置

父级:

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css"/>
    <title>{% block title %}My amazing site{% endblock %}</title>
</head>

<body>
<div id="sidebar">
    {% block sidebar %}
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/">Blog</a></li>
        </ul>
    {% endblock %}
</div>

<div id="content">
    {% block content %}{% endblock %}
</div>
</body>
</html>

子级模板:

{% extends "base.html" %}
 
{% block title %}My amazing blog{% endblock %}
 
{% block content %}
{% for entry in blog_entries %}
    <h2>{{ entry.title }}</h2>
    <p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}

上面的sidebar位置并没有被子级覆盖所以还是现实了父级的内容,就像class的继承一样子级可以选择覆盖也可以选择不覆盖,

子级中可以在某个block块中引如父级原本的内容

{% block title %}
	{{ block.super }}
{% endblock %}

注意:extends 语句必须位于第一行

Django单元测试

当我们想要针对某个部分代码进行测试时,直接执行Django相关的代码将无法成功,因为Django没有被正确的初始化,所以如果我们的测试代码中包含了Django提供的任何功能我们就需要在测试前线初始化Django环境

在任意测试文件中添加一下代码

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "项目名称.settings")
    import django
    django.setup()

#    测试代码:
    from app01 import models
    books = models.Book.objects.all()
    print(books)

注意:导入的代码也必须放到setup()之后执行!

posted @ 2019-09-17 01:32  CoderJerry  阅读(395)  评论(0编辑  收藏  举报