django(8)

1、django的简介

在这里插入图片描述

浏览器与服务器之间遵循的一个协议: HTTP协议

服务器与应用程序框架之间:wsgi

1.1 Django的简介

Django是一个重量型框架

主要目的:简便快捷开发

Django基于MVC模式,但是它是MVT模式的

核心:解耦(高内聚,低耦合)
在这里插入图片描述
在这里插入图片描述
MVC设计的框架

(1)重量级框架

(2)MVT模式

MVC :

定义:

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。

在这里插入图片描述

2、创建项目的环境

虚拟环境:

项目所需要的第三方软件环境相互隔离

安装虚拟环境

sudo pip3 install virtualenv
sudo pip3 install virtualenvwrapper

创建虚拟环境

mkvirtualenv dj_3 -p python

切换虚拟环境

workon dj_3

删除虚拟环境

rmvirtualenv dj_3

退出环境

deactivate

3、安装Django,初步讲解

3.1安装Django:

pip install django==安装的版本号
一般安装在虚拟环境中

3.2创建项目:

django-admin startproject 项目名称
里面有一个和项目同名的文件夹,内部包含的是项目的配置文件
settings.py项目的整体配置文件
urls.py项目的URL配置文件
wsgi.py是项目与服务器的入口

manage.py是项目运行的入口

3.3 运行服务器

python manage.py runserver
注意点:如果不指定端口,默认是8000
python manage.py runserver 5000

3.4 成功的界面:
在这里插入图片描述

3.5 创建模块

python manage.py startapp 模块名

user子模块

admin.py 后台管理站点的配置

apps.py 当前子应用的信息

models.py 保存书写的数据库模型

text.py 用来保存开发测试用例的,进行单元测试

views.py 书写逻辑,用来保存视图。

3.6 创建模块之后,注册子模块,注册到同名文件夹的setting中

pai0805中的settings.py

INSTALLED_APPS = [
	"django.contrib.admin",
	"django.contrib.auth",
	"django.contrib.contenttypes",
	"django.contrib.sessions",
	"django.contrib.messages",
	"django.contrib.staticfiles",
	"user.apps.UserConfig"

]

3.7 简单体验数据库交互

3.7.1 定义表与字段

一张表对应了一个类

user中models.py

class UserInfo(models.Model):
	name = models.CharField(max_length=10)
	age = models.IntegerField()
	phone = models.CharField(max_length=11)
	
	def __str__(self):
	return self.name
	
	
类名对应的是表名
属性名对应的是字段名
注意点:继承于Model

3.7.2 迁移数据库表

1、生成迁移文件
python manage.py makemigration
2、执行迁移文件
pyhton manage.py migrate
迁移成功并且执行迁移之后,才真正在数据库产生表

3.7.3 创建超级权限账号和修改界面语言

pai0805中的settings.py



LANGUAGE_CODE = "zh-Hans"

TIME_ZONE = "Asia/Shanghai"

python manage.py createsuperuser

3.7.4 数据库的配置

1、设置数据库

pai0805中的settings.py

DATABASES={
	"default":{
		"ENGINE":"django.db.backends.mysql",#这个参数是设置数据库
		"NAME":"数据库名字",#使用的数据的名字
		"USER":"root",#数据库的用户名
		"PASSWORD":"mysql",
		"HOST":"127.0.0.1",#数据库主机的地址
		"PORT":3307,#数据库的端口
}
}

2、添加驱动

pai0805中的 __init__.py

import pymysql

pymysql.install_as_MySQLdb()

3、在admin站点管理中注册models

user 中 admin.py

from django.contrib import admin
from user.models import UserInfo  #导入

admin.site.register(UserInfo)

3.7.5 查看

use pai0805;

show tables;

在这里插入图片描述

其中user_userinfo表是创建的

3.8 实现网站的用户信息展示

3.8.1 定义视图,处理业务

user中view.py
视图的作业:创建业务逻辑,完成路径--视图的匹配,返回给对应路径的数据或者网页
视图函数
def userinfo():
	"""这个视图函数实现的是暂时用户的信息,匹配的路径/userinfo"""
	pass
	
def index(request):
    print("----------------------")
    print(type(request))
    print(request)
    # return "hello world"
    return HttpResponse("hello world")


Request是一个请求对象,可以是别的名字,但是一般是request,每一个视图函数都必须要有一个参数来承接

报错:
在这里插入图片描述

注意点:视图函数必须要有一个响应对象返回
在这里插入图片描述

视图函数,至少需要一个参数来接收Django传递过来的参数

3.8.2 配置路由URL

1、路由配置第一种方法:

from user import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r"^index/$",views.indesx),
]

2、第二种方法(分模块再集中)

(1)在项目所在的同名文件夹的urls里面配置include()将各个模块的URL加载

(2)在各个模块的URL里面进行配置URL-----VIEWS

(3)项目同名的urls值完成路由的分发匹配过程

真正匹配路径在各个模块的URL下面

视图与路径的匹配
在这里插入图片描述

3.9 配置html模板文件

3.9.1 创建一个名字叫templates的文件夹

3.9.2 配置文件夹的查找路径

在同名文件夹中settings.py中的
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        #设置模板的查找路径
        'DIRS': [os.path.join(BASE_DIR,"templates")],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

打印路径:
在这里插入图片描述

3.9.3 返回模板到前端

def show(request):
    # #创建连接
    # con = pymysql.connect(host="localhost", user="root", password="535897", database="pai0805_2", port=3307,charset="utf8")
    # #获取光标对象,操作句柄
    # cur=con.cursor()
    # #写查询语句
    # sql1="select * from user_userinfo;"
    # #进行查询
    # ret_num1 =cur.execute(sql1)
    # ret = cur.fetchall()
    # return HttpResponse(ret)
    from user.models import UserInfo

    data = UserInfo.objects.all()
    for i in data:
        print(i)
	#可以在info.html中设置界面的字体,格式
    return render(request,"info.html",{"data":data[0],"data1":data[1]})

3.10 查看数据库的日志

sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
#修改mysql中的配置文件让日志可以查看
将
general_log_file        = /var/log/mysql/mysql.log
general_log             = 1
前面的#除去
sudo service mysql restart
#重启mysql服务器
sudo tail -f /var/log/mysql/mysql.log
最后打开就能开启日志

3.11 表中html文件和静态文件

在settings.py中
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        #设置模板的查找路径在templates中
        'DIRS': [os.path.join(BASE_DIR,"templates")],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR,"static")
]
#静态文件包括js,图片,css文件
#设置静态文件模板的查找路径在static中
  1. 模型的增删改查(ORM模块)

4.1 数据的查询

4.1.1 查询所有的数据

from user.models import UserInfo

data = UserInfo.objects.all()

格式: 模型类名.objects.all()

查询多条数据

4.1.2 按照指定条件查询

def get(self,*args,**kwargs):
	
注意点:
1、get()获取的是指定条件符合数据,只有一条

4.1.3 计数语句

data = Userinfo.objects.count()
返回的是数据的条数

4.1.4 符合条件的多条数据查询

1、比较查询

符合条件的多条语句
1、比较查询  大于 gt greater then
data = UserInfo.objects.filter(id_gt = 1)
id大于等于3 gte
data = UserInfo.objects.filter(id_gte = 3)
id小于1的人
data = UserInfo.objects.filter(id_lt=1)
id小于等于1的人
data = UserInfo.objects.filter(id_lte=1)

注意点:
filter既可以查询一条语句,也可以查询多条语句
但是查询的结果放在一个列表里面、
如果查询不到任何满足条件的数据,则返回的是一个空列表

2、模糊查询

查找名字中带有王的人
data = UserInfo.objects.filter(name_contains = "王")

查找姓王的人
data = UserInfo.objects.filter(name_startswith = "王")

4.1.5 关于外键的查询

UserInfo.objects.all()

add.userinfo_set.all()
#未知

4.2 数据的增加

4.2.1 第一种方式

通过创建对象,进行属性设置,最后再保存的方式

 book1 = BookInfo(booknane="红楼梦",time="1700-1-1")
 book1.save()

4.2.2 第二种方式(重点)

 PersonInfo.objects.create(name="猴子",weapon="棒子",book=book1[0])

4.3 当数据表存在数据的时候,对于修改数据表

第一种
book2 = BookInfo.objects.get(pk=3)
book2.time="1973-1-1"
book2.save()

第二种
BookInfo.objects.filter(name="三国演绎").update(time="1973-10-01")

4.4关于数据库表名的设计

如果没有指定数据库的表名,则MYSQL中会以应用名_模型类名的小写作为数据库的表名

设计数据库表名:

在模型类的内部进行表的设计

class PersonInfo(models.Model):
    gender_choice = (
        (0,"boy"),
        (1,"girl"),
    )

	#名字
    name = models.CharField(max_length=30,verbose_name="人物名字")
	# 性别
    gender = models.SmallIntegerField(choices=gender_choice,default=0,verbose_name="性别")
	# 武器与技能
    weapon = models.CharField(max_length=300, verbose_name="武器")
	# 逻辑删除
    is_delete = models.BooleanField(default=False, verbose_name="逻辑删除")

    book = models.ForeignKey(BookInfo,on_delete=models.DO_NOTHING,verbose_name="外间图书")

    def __str__(self):

        return self.name
    class Meta:
        db_table = "personinfo"
        #指定数据库的表名
        verbose_name = "人物信息"
        #修改后台管理中心中数据库表的名字
        verbose_name_plural=verbose_name
        #将复数形式去除
        
注意点:
修改数据库的表名需要迁移

4.5 关于模型与数据库数据的关系

from book.models import BookInfo,PersonInfo#测试的时候,需要将使用到的模型导入

add = BookInfo(name="书名",time="出版")#数据库的一条数据,其实就是模型类的一个实例
add.save()#保存,不保存不生效

当存在外键的时候:

person1 = PersonInfo()
person1.name="名字"
person1.book = add
person1.save()
Addinfo这个属性是一个外键,赋值的时候需要以对象进行赋值

4.6 关于orm模块设置数据类型
在这里插入图片描述

约束

primary_key = False 主键
null = False 允许为空,默认为false,数据库的角度来说的
blank = False 字段允许为空白,表单验证
db_column = None字段的名称,没有指定的话,使用属性的名字
db_index=Flase给该字段设置索引
unique =Flase 说明该字段在这个表里面具有唯一性
delfault = 设置默认值

例子:设置字段名字
在这里插入图片描述

4.7 模型的相关字段

name = models.CharField(db_column="username",max_length=20)
phone = models.CharField(max_length=11,unique=True)
pwd = models.CharField(max_length=8,default="123")
pwd= mdoels.IntegerField(default="123")
models.AutoField()如果不指定,则自动指定属性名为id的自增长属性,一般不指定
models.BooleanField()布尔值
models.NullBolleanField()布尔值加null
models.TextField()大文本字段,字符在4000向上
models.DecimalField(max_digits=总位数,decimal_places=小数位数)#十进制浮点型
models.DateField()表示的是日期
models.TimeField()表示的是时间
models.DateTimeField()表示的是日期加时间
models.FileField()上传文件的字段
models.ImageField()对上传的文件进行校验,保证是有效图片

4.8 重写父类的manager方法

4.8.1重写父类的manager方法

class BookManager(models.Manager):
    #重写管理器方法
    def all(self):
        return super().filter(is_delete=False)
    def mycreate(self,name,time):
        '''
        自定义创建一个新的模型对象
        :return:
        '''
        print("这是进行定义的创建方法")
        print(self.model)
        obj=self.model()
        obj.name=name
        obj.time=time
        obj.save()
        return obj
        # book=BookInfo()
        # book.name="123"
        # book.time="2019-9-28"
        # book.save()

4.8.2 在对应的模型类里面进行注册(实例化)

new_objects = BookManager()

4.8.3 进行使用

BookInfo.new_objects.mycreate("123","2019-9-28")

4.9 重写all方法

class BookManager(models.Manager):
    #重写管理器方法
    def all(self):
        return super().filter(is_delete=False)
    def mycreate(self,name,time):
        '''
        自定义创建一个新的模型对象
        :return:
        '''
        print("这是进行定义的创建方法")
        print(self.model)
        obj=self.model()
        obj.name=name
        obj.time=time
        obj.save()
        return obj
        # book=BookInfo()
        # book.name="123"
        # book.time="2019-9-28"
        # book.save()

class BookInfo(models.Model):
    # 图书名
    name = models.CharField(max_length=20,verbose_name="书籍名字")
    # 出版时间
    time = models.DateField(verbose_name="出版时间",null=True)
    """
    auto_now=False,表示每次保存对象时,自动设置该字段为当前时间,用于作最后一次修改的时间
    auto_now_add=False表示当前对象第一次被创建自动设置当前时间,用于创建的时间戳
    当前两个参数是相互排斥的
    """
    # 阅读量
    count = models.IntegerField(verbose_name="阅读量",default=0)
    # 销量
    seltcount = models.IntegerField(verbose_name="销量",default=0)
    # 逻辑删除
    is_delete = models.BooleanField(default=False,verbose_name="逻辑删除")

    class Meta:
        db_table = "bookinfo"
        #指定数据库的表名
        verbose_name = "书籍"
        #修改后台管理中心中数据库表的名字
        verbose_name_plural=verbose_name
        #将复数形式去除

    def __str__(self):

        return self.name
    new_objects = BookManager()
    #创建一个新的实例化对象

4.12 前后端不分离

html文件
<script src="/static/jquery-3.1.1.js"></script>
<script>
    $(function() {
        $(".box2").click(function () {
            console.log("abc");
            $(this).siblings(".box4").toggle();
        });
        $(".box1").click(function () {
            $(".box6").toggle()
        })
    })
</script>
<style>
    .box2{
    }
    .box4{
        display: none;
    }
    .box6{
        display: none;
    }
</style>
<body>
    <button class="box1">查询</button>
    <div class = "box6">
    {% for i in data %}
        <ul>{{ i }} <button class="box2">详情</button><a href="http://127.0.0.1:5000/book/shanchu/id={{ i.id }}"><button>删除</button></a>
        <li class="box4">{{ i.name }}</li>
        <li class="box4">{{ i.time }}</li>
        <li class="box4">{{ i.seltcount }}</li>
        <li class="box4">{{ i.count }}</li>
        </ul>
    {% endfor %}
    </div>

views文件

def bookinfo4(request):
    books = BookInfo.new_objects.all()
    # books = dict(i.name:i for i in books)
    context = {"data":books}
    #将列表转换成字典
    print(context)
    print(type(books))
    return render(request,"chaxun.html",context)
def shanchu(request,id=None):
    print(request)
    print(id)
    book = BookInfo.new_objects.get(id=id)
    print(book)
    print(book.is_delete)
    # book.is_delete = True
    # book.save()
    book.delete()
    return HttpResponse("ok")

5、视图与模型的结合使用

以简单的用户注册为例

5.1 设计展示用户操作页面

<form action="http://127.0.0.1:5000/huoqu" method="get">
        请输入用户名:<input type="text" name="username">
        请输入用户密码:<input type="password" name="password">
        <input type="submit">


    </form>

5.2 设置页面对应的路径,当用户请求的时候,展示
在这里插入图片描述

注意点:视图函数至少有一个参数

必有要有响应对象返回

return HttpResponse("ok")

5.3 HTTP请求对象

def index(request):
	print(request)
	return HttpResponse("123")

注意点:

1、这是一个HTTP请求对象

2、这个请求对象里面封装的是请求信息,包括请求的方式,数据等

5.3.1 request.body

当非表单数据,request.body 来获取到请求体数据,但是,body获取的数据是bytes类型

def index(request):
	print(request.body)
	return HttpResponse("123")
	
一般情况下,都是json

5.3.2 request.GET

获取到的都是类字典类型

<script src="/static/jquery-3.1.1.js"></script>
<script>
    $(function () {
        $(".btn").click(function () {
            console.log("123");
            var username = $("#username").val();
            var password = $("#password").val();
            console.log(username,password);
            var data = {
                "username":username,
                "password":password
            };

        $.ajax({
            url:"http://127.0.0.1:5000/username/huoqu1/",
            type:"GET",
            data:data,
            dataType:"json",
            success:function () {
                console.log("666666")
            }
        })
    })
        })
</script>
<body>


        请输入用户名:<input type="text" name="username" id="username">
        请输入用户密码:<input type="password" name="password" id="password">
        <button class="btn">提交</button>
        
注意点:会友crsf问题解决方法,注释掉
    # 'django.middleware.csrf.CsrfViewMiddleware',
    或者在html文件中加上{% crsf %}

5.3.3 request.POST

获取到的都是类字典类型

<script src="/static/jquery-3.1.1.js"></script>
<script>
    $(function () {
        $(".btn").click(function () {
            console.log("123");
            var username = $("#username").val();
            var password = $("#password").val();
            console.log(username,password);
            var data = {
                "username":username,
                "password":password
            };
            {#data = json.stringify(data);#}
            {#$.ajax({#}
            {# 这是ajax的提交路径 #}
            {#    url:"http://127.0.0.1:5000/username/huoqu1/",#}
            {#    type:"POST",#}
            {# 这是ajax的请求防护斯 #}
            {#    data:data,#}
            {# 这是携带的数据 #}
            {#    dataType:"json",#}
            {#  #}
            {#    success:function () {#}
            {#        console.log("6666")#}
            {#    },#}
            {#    error:function () {#}
            {#        console.log("9999")#}
            {#    }#}
    })
        })
</script>
<body>


        请输入用户名:<input type="text" name="username" id="username">
        请输入用户密码:<input type="password" name="password" id="password">
        <button class="btn">提交</button>

5.3.4 request.META获取请求头的数据

def index(request):
	print(request.META)
	requset.META这个属性,用来获取的是请求头header部分所有的数据
	他是一个字典类型

5.4 httpresponse对象

5.4.1 render函数的具体使用

def render(request, template_name, context=None, content_type=None, status=None, using=None):
    """
    Returns a HttpResponse whose content is filled with the result of calling
    django.template.loader.render_to_string() with the passed arguments.
    """
    content = loader.render_to_string(template_name, context, request, using=using)
    return HttpResponse(content, content_type, status)

注意点:

1、 Render函数适用的范围是:有网页,有填充网页的数据

2、 Render这个函数的第一个参数是当前路径的请求对象

3、 Context这个参数指向的是填充网页的数据,注意它的格式必须是字典

5.4.2 HTTPresponse

def __init__(self, content_type=None, status=None, reason=None, charset=None):
        # _headers is a mapping of the lower-case name to the original case of
        # the header (required for working with legacy systems) and the header
        # value. Both the name of the header and its value are ASCII strings.
        self._headers = {}
        self._closable_objects = []
        # This parameter is set by the handler. It's necessary to preserve the
        # historical behavior of request_finished.
        self._handler_class = None
        self.cookies = SimpleCookie()
        self.closed = False
        if status is not None:
            try:
                self.status_code = int(status)
            except (ValueError, TypeError):
                raise TypeError('HTTP status code must be an integer.')

            if not 100 <= self.status_code <= 599:
                raise ValueError('HTTP status code must be an integer from 100 to 599.')
        self._reason_phrase = reason
        self._charset = charset
        if content_type is None:
            content_type = '%s; charset=%s' % (settings.DEFAULT_CONTENT_TYPE,
                                               self.charset)
        self['Content-Type'] = content_type

注意点:

1、适用范围,返回的是让浏览器或者客户端展示的字符串

5.5 csrf的问题

5.5.1 在表单中增加

<form action="http://127.0.0.1:5000/username/huoqu" method="get">
        请输入用户名:<input type="text" name="username">
        请输入用户密码:<input type="password" name="password">
        <input type="submit">


    </form>

5.6 关于路径匹配的补充知识

5.6.1 路径匹配

url(r"^userna(\w+)/",views.username),
url(r"^huoqu/",views.huoqu),

Exception Value:	
username() takes 1 positional argument but 2 were given

URL函数正则匹配,如果需要匹配组,则匹配出来的组信息,会自动传递给对应的视图函数

url(r"^userna(\w+)/"[0],views.username),

5.6.2 关键字参数

urlpatterns = [
    url(r"(?P<city>city)/(?P<cname>\w+)/",views.username),
    url(r"^huoqu/",views.huoqu),
    url(r"^search/$",views.search),
    url(r"^data/$",views.data),
]

def username(request,city,cname):
    # #创建连接
    # con = pymysql.connect(host="localhost", user="root", password="535897", database="pai0805_2", port=3307,charset="utf8")
    # #获取光标对象,操作句柄
    # cur=con.cursor()
    # #写查询语句
    # sql1="select * from user_userinfo;"
    # #进行查询
    # ret_num1 =cur.execute(sql1)
    # ret = cur.fetchall()
    # return HttpResponse(ret)
    print(city)
    print(cname)
    return render(request,"register.html")

注意点:?P

5.7 查询对象的获取

try:
    book = BookInfo.objects.get(name="三国演绎")
except Exception as e:
	print(e)
#通过书籍查找该书籍查找该书籍中的人物
print(book)
peo = book.personinfo_set.all()
print(peo)
obj = PersonInfo.objects.get(name="猴子")
print(obj.book.id)
print(type(obj.book))
#关联过滤查询

book_obj = BookInfo.objects.filter(personinfo__name__exact="猴子")
print(book_obj)
#关联模型类的小写--属性名--条件筛选运算符=值
per_obj =  PersonInfo.objects.filter(book__seltcount__gt=0)
print(per_obj)

注意点:

1、request。GET\POST 获取的都是QUERYDICT对象

2、获取QueryDict的某一个值对应的对象,使用的是get ,但是如果一个键名对应多个值的时候,get只能获取一个,并且是最后一个

3、如果想要获取多个值,需要使用的是getlist

4、get \getlist 都可以设置默认值,当获取不到数据的时候(当键名不存在获取或者getlist获取到空列表的时候)

5.8 form表单的提取

注意点:

当name值相同的时候,可以设置value的值

5.9 关于浏览器与服务器之间的状态保持

浏览器与服务器之间是无状态的

Ssession:存在服务器一端

Cookie:存在浏览器一端

关系:一一对应的

5.9.1 cookie

特点:

1、键值结构

2、cookie是基于域名安全的,不同域名之间cookie是不可以进行相互的访问的

3、当浏览器请求某一个网站,会将与当前网站所有的cookie提交给服务器

4、cookie数据不允许存储敏感信息,密码,支付宝密码

5.9.2 cookie的设置
在这里插入图片描述

设置完成之后:
在这里插入图片描述

创建cookie

Response.set_cookie()

删除cookie

Resposne.delete_cookie()

读取cookie

Request.COOKIES.get(键名)

Cookie虽然存储在浏览器一端,但是,它是在服务器一端进行设置的,

5.9.2 session

'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',

1、如果需要使用数据库存储session,安装应用

'django.contrib.sessions',

2、在数据库中
在这里插入图片描述

注意点:在Django项目中,session的引擎没有设置,因为则是默认的存储方式

5.10 一些查询和判断

Bookinfo.objects.exclude(id__exact=1)
查询的是book表中id不等于1的对象返回的是一个queryset

判断查询集中是否有数据
Bookinfo.objects.exclude(id__exact=1).length

重用查询集
使用books=BookInfo.objects.all()

5.11 中间件的使用

5.11.1创建一个中间件

def my_middleware(get_response):
    print("init被调用")
    def middleware(request):
        print("before request")
        response = get_response(request)
        print("after response")
        return response
    return middleware

5.11.2注册进settings中

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    "book.middleware.my_middleware",#注册进settings中
]

5.11.3 中间件的顺序

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    "book.middleware.my_middleware",
]

在处理request是先从上往下
处理完request之后返回response时从下往上

5.12 类视图

需要先导包
from django.views.generic import View
然后创建类
class Showusers(View):
    def get(self,request):
        return HttpResponse("这是get方法")
    def post(self,request):
        return HttpResponse("这是post方法")

在URL中的设置
url("^users/$",views.Showusers.as_view()),

之后再postman之中
使用不同的连接方式
会显示不同的内容

6 DRF 架构的使用

6.1 restful风格设计

域名:尽量的将API部署到专用的域名下面

http://api.example.com

版本:将版本号放入到url中

http://passport.baidu.com/v2/api/?login

路径:

资源作为网址一般情况下只能由名词

GET /goods 展示所有的商品(列表页)

GET /goods/id (详情页)

PUT /goods/9 (更新某一个商品)
6.2 安装第三方包

pip install djangorestframework

6.3 添加应用

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    "user.apps.UserConfig",
    "book.apps.BookConfig",
    "username.apps.UsernameConfig",
    "rest_framework",
]

6.4 序列化与反序列化

将模型数据转化成json数据 — 序列化

from rest_framework import serializers
from book.models import BookInfo


class UserSeralizer2(serializers.Serializer):
    name = serializers.CharField(max_length=20)
    time = serializers.DateField()
    count = serializers.IntegerField()
    seltcount = serializers.IntegerField()
    is_delete = serializers.BooleanField()

    def get(self,request):
        user_obj = BookInfo.new_objects.get(id=1)
        print(user_obj)
        ser1_obj = UserSeralizer(user_obj)

        return JsonResponse(ser1_obj.data)

from rest_framework.renderers import JSONRenderer

json = JSONRenderer().render(serializer.data)
json
# b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'

将接收到的json数据转化成模型 — 反序列化

        # user_obj = BookInfo.new_objects.get(id=1)
        # print(user_obj)
        # ser1_obj = UserSeralizer(user_obj)
        # serializer = UserSeralizer(data=ser1_obj.data)
        # print(serializer.is_valid())
        # # True
        # print(serializer.validated_data)
        # return JsonResponse(serializer.validated_data, safe=False)

6.5 保存实例

如果我们希望能够基于经过验证的数据返回完整的对象实例,则需要实现.create()和.update()方法之一或两者。

如果您的对象实例与Django模型相对应,则还需要确保这些方法将对象保存到数据库中。例如,如果Comment是Django模型,则方法可能如下所示:

	def create(self, validated_data):
        book_obj = BookInfo.objects.create(**validated_data)
        return book_obj
    def update(self, instance, validated_data):
        instance.name = validated_data.get("name")
        instance.time = validated_data.get("time")
        instance.count = validated_data.get("count")
        instance.seltcount = validated_data.get("seltcount")
        instance.is_delete = validated_data.get("is_delete")
        instance.save()
        return instance

现在,在对数据进行反序列化时,我们可以.save()基于已验证的数据调用以返回对象实例。

    def put(self,request):
        user_obj_1 = BookInfo.new_objects.get(id=8)
        json_str = request.body.decode()
        print(json_str)
        # import json
        # json_str1 = json.load(json_str)
        # print(json_str1)

        json_list = json_str.split("------")[1:6]
        print(json_list)
        param_list = []
        for each in json_list:
            # print(each.split("\r\n"))
            param_list.append(each.split("\r\n")[3])

        print(param_list)
        query_dict = {"name":param_list[0],"time":param_list[1],"count":param_list[2],"seltcount":param_list[3],"is_delete":param_list[4]}
        print(query_dict)
        ser_2 = UserSeralizer(user_obj_1,data=query_dict)
        ser_2.is_valid()
        ser_2.save()

        return HttpResponse("更新")

调用.save()将创建一个新实例,或更新一个现有实例,具体取决于在实例化序列化程序类时是否传递了一个现有实例:

# .save() will create a new instance.
serializer = CommentSerializer(data=data)

# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)

无论是.create()和.update()方法是可选的。根据序列化程序类的用例,您既可以实现它们之一,也可以两者都不实现。

将其他属性传递给 .save()

有时,您希望视图代码能够在保存实例时注入其他数据。这些附加数据可能包括诸如当前用户,当前时间或不属于请求数据一部分的任何其他信息。

您可以通过在调用时包含其他关键字参数来实现.save()。例如:

serializer.save(owner=request.user)

6.6验证方式

反序列化数据时,您始终需要先调用,is_valid()然后再尝试访问经过验证的数据或保存对象实例。如果发生任何验证错误,则该.errors属性将包含一个字典,代表产生的错误消息。例如:

serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'email': ['Enter a valid e-mail address.'], 'created': ['This field is required.']}

6.6.1 现场级验证

from rest_framework import serializers
from book.models import BookInfo


class UserSeralizer2(serializers.Serializer):
    name = serializers.CharField(max_length=20)
    time = serializers.DateField()
    count = serializers.IntegerField()
    seltcount = serializers.IntegerField()
    is_delete = serializers.BooleanField()

    def validate_count(self, value):
        print("1")
        if value == 0:
            return value
        else:
            raise serializers.ValidationError("4564756")

6.6.2 对象级验证

from rest_framework import serializers
from book.models import BookInfo


class UserSeralizer2(serializers.Serializer):
    name = serializers.CharField(max_length=20)
    time = serializers.DateField()
    count = serializers.IntegerField()
    seltcount = serializers.IntegerField()
    is_delete = serializers.BooleanField()

    def validate(self, attrs):
        print("***************************")
        print("2")
        print(attrs)
        import re
        if len(attrs["name"])>12 or attrs["count"]>=200 or re.search(r"^[^4]]",attrs["name"]):
            raise serializers.ValidationError("4564756")
        return attrs

6.6.3 验证者

序列化程序上的各个字段可以包含验证器,方法是在字段实例上声明它们,例如:

def multiple_of_ten(value):
    if value % 10 != 0:
        raise serializers.ValidationError('Not a multiple of ten')

class GameRecord(serializers.Serializer):
    score = IntegerField(validators=[multiple_of_ten])

UniqueValidator

该验证器可用于unique=True对模型字段实施约束。它带有一个必需的参数和一个可选的messages参数:

  • queryset 必需 -这是应针对其强制执行唯一性的查询集。
  • message -验证失败时应使用的错误消息。
  • lookup-用于查找具有已验证值的现有实例的查找。默认为’exact’。

该验证器应应用于序列化器字段,如下所示:

from rest_framework.validators import UniqueValidator

slug = SlugField(
    max_length=100,
    validators=[UniqueValidator(queryset=BlogPost.objects.all())]
)

UniqueTogetherValidator

该验证器可用于unique_together对模型实例施加约束。它具有两个必需参数和一个可选messages参数:

  • queryset 必需 -这是应针对其强制执行唯一性的查询集。
  • fields 必填 -字段名称的列表或元组,应组成唯一的集合。这些必须作为字段存在于序列化程序类中。
  • message -验证失败时应使用的错误消息。

验证器应应用于序列化器类,如下所示:

from rest_framework.validators import UniqueTogetherValidator

class ExampleSerializer(serializers.Serializer):
    # ...
    class Meta:
        # ToDo items belong to a parent list, and have an ordering defined
        # by the 'position' field. No two items in a given list may share
        # the same position.
        validators = [
            UniqueTogetherValidator(
                queryset=ToDoItem.objects.all(),
                fields=['list', 'position']
            )
        ]

6.7 ModelSerializer

通常,您会需要与Django模型定义紧密映射的序列化程序类。

本ModelSerializer类提供了一个快捷方式,可以让你自动创建一个Serializer类对应于模型字段的字段。

该ModelSerializer班是一样的常规Serializer类,不同之处在于:

  • 它将根据模型自动为您生成一组字段。
  • 它将自动为序列化器生成验证器,例如unique_together验证器。
  • 它包括简单的默认实现.create()和.update()。

声明ModelSerializer如下所示:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ['id', 'account_name', 'users', 'created']

默认情况下,该类上的所有模型字段都将映射到相应的序列化器字段。

任何关系(例如模型上的外键)都将映射到PrimaryKeyRelatedField。默认情况下不包括反向关系,除非按照序列化器关系文档中的指定明确包含反向关系。

检查一个 ModelSerializer

序列化程序类会生成有用的详细表示形式字符串,使您可以全面检查其字段的状态。这ModelSerializers在您要确定要为您自动创建哪些字段和验证器的地方工作时特别有用。

为此,请使用打开Django Shell,python manage.py shell然后导入序列化程序类,实例化它,然后打印对象表示形式…

>>> from myapp.serializers import AccountSerializer
>>> serializer = AccountSerializer()
>>> print(repr(serializer))
AccountSerializer():
    id = IntegerField(label='ID', read_only=True)
    name = CharField(allow_blank=True, max_length=100, required=False)
    owner = PrimaryKeyRelatedField(queryset=User.objects.all())

指定要包括的字段
如果只希望在模型序列化器中使用默认字段的子集,则可以使用fields或exclude选项,就像使用一样ModelForm。强烈建议您使用fields属性显式设置应序列化的所有字段。这将减少在模型更改时导致意外暴露数据的可能性。

例如:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ['id', 'account_name', 'users', 'created']

您也可以将fields属性设置为特殊值,'all’以指示应使用模型中的所有字段。

例如:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = '__all__'

您可以将exclude属性设置为要从序列化器中排除的字段列表。

例如:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        exclude = ['users']

在上面的例子中,如果Account模型具有3个字段account_name,users和created,这将导致在字段account_name和created被序列化。

fields和exclude属性中的名称通常会映射到模型类上的模型字段。

另外,fields选项中的名称可以映射到属性或方法,这些属性或方法不包含模型类上存在的参数。

从3.3.0版开始,必须提供属性fields或之一exclude。
6.8 基于类的视图

REST框架提供了一个APIView类,该类继承了Django的View类。

APIView类与常规View类在以下方面有所不同:

  • 传递给处理程序方法的请求将是REST框架的Request实例,而不是Django的HttpRequest实例。
  • 处理程序方法可能返回REST框架的Response而不是Django的HttpResponse。该视图将管理内容协商并在响应上设置正确的渲染器。
  • 任何APIException异常都将被捕获并调解为适当的响应。
  • 在将请求分派到处理程序方法之前,将对传入的请求进行身份验证并进行适当的权限和/或限制检查。

使用APIView该类与使用常规View类几乎相同,通常,传入的请求将分派到适当的处理程序方法,例如.get()或.post()。另外,可以在控制API策略各个方面的类上设置许多属性。

6.9 通用视图

基于类的视图的主要优点之一是它们允许您组成一些可重用行为的方式。REST框架通过提供许多提供常用模式的预构建视图来利用此优势。

REST框架提供的通用视图使您可以快速构建与数据库模型紧密映射的API视图。

如果通用视图不符合您的API的需求,则可APIView以下拉到使用常规类,或者重用通用视图使用的mixins和基类来组成自己的可重用通用视图集。

6.9.1 API参考

GenericAPIView
该类扩展了REST框架的APIView类,为标准列表和详细信息视图添加了通常需要的行为。

提供的每个具体的通用视图都是通过将GenericAPIView,和一个或多个mixin类组合而构建的。

属性
基本设定:

以下属性控制基本视图行为。

queryset-应该用于从该视图返回对象的查询集。通常,您必须设置此属性或重写get_queryset()方法。如果要覆盖视图方法,则必须进行调用get_queryset()而不是直接访问此属性,这queryset将被评估一次,并且这些结果将被缓存用于所有后续请求,这一点很重要。
serializer_class-应该用于验证和反序列化输入以及序列化输出的序列化器类。通常,您必须设置此属性或重写get_serializer_class()方法。
lookup_field-应该用于执行单个模型实例的对象查找的模型字段。默认为’pk’。请注意,使用超链接的API时,您需要确保双方的API意见和串行类设置查找字段,如果你需要使用一个自定义值。
lookup_url_kwarg-用于对象查找的URL关键字参数。URL conf应包含与此值相对应的关键字参数。如果未设置,则默认使用与相同的值lookup_field。
分页:

与列表视图一起使用时,以下属性用于控制分页。

pagination_class-对列表进行分页时应使用的分页类。默认值为与DEFAULT_PAGINATION_CLASS设置相同的值’rest_framework.pagination.PageNumberPagination’。设置pagination_class=None将禁用此视图上的分页。
筛选:

filter_backends-应该用于过滤查询集的过滤器后端类的列表。默认值为与DEFAULT_FILTER_BACKENDS设置相同的值。
方法
基本方法:

get_queryset(self)
返回应用于列表视图的查询集,该查询集应用作详细视图中的查询的基础。默认情况下返回queryset属性指定的查询集。

应始终使用此方法,而不是self.queryset直接访问此方法,因为它self.queryset只会被评估一次,并且那些结果将为所有后续请求缓存。

可以重写以提供动态行为,例如返回特定于发出请求的用户的查询集。

class GericInfo1(generics.ListCreateAPIView):
    queryset = BookInfo.new_objects.all()
    serializer_class = UserSeralizer

6.10 视图集

Django REST框架允许您在称为的单个类中将一组相关视图的逻辑组合在一起ViewSet。在其他框架中,您可能还会发现概念上类似的实现,这些实现的名称类似于“资源”或“控制器”。

甲ViewSet类只是一种类型的基于类的视图,即不提供任何方法的处理程序,例如.get()或.post(),而是提供操作,如.list()和.create()。

使用方法,a的方法处理程序ViewSet仅在完成视图时绑定到相应的动作.as_view()。

通常,您将向路由器集注册视图集,而不是在urlconf中的视图集中显式注册视图,而是自动为您确定urlconf。


让我们定义一个简单的视图集,可用于列出或检索系统中的所有用户。

from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from myapps.serializers import UserSerializer
from rest_framework import viewsets
from rest_framework.response import Response

class UserViewSet(viewsets.ViewSet):
    """
    A simple ViewSet for listing or retrieving users.
    """
    def list(self, request):
        queryset = User.objects.all()
        serializer = UserSerializer(queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        queryset = User.objects.all()
        user = get_object_or_404(queryset, pk=pk)
        serializer = UserSerializer(user)
        return Response(serializer.data)

如果需要,可以将此视图集绑定到两个单独的视图中,如下所示:(方法一)

user_list = UserViewSet.as_view({'get': 'list'})
user_detail = UserViewSet.as_view({'get': 'retrieve'})

通常,我们不这样做,而是向路由器注册视图集,并允许自动生成urlconf。(方法二)

from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
urlpatterns = router.urls

您通常不希望编写自己的视图集,而是要使用提供默认行为集的现有基类。例如:

class UserViewSet(viewsets.ModelViewSet):
    """
    A viewset for viewing and editing user instances.
    """
    serializer_class = UserSerializer
    queryset = User.objects.all()

与使用ViewSet类相比,使用类有两个主要优点View。

  • 重复的逻辑可以合并为一个类。在上面的示例中,我们只需要指定queryset一次,它将在多个视图中使用。
  • 通过使用路由器,我们不再需要自己处理URL conf。

两者都需要权衡。使用常规视图和URL conf更加明确,并给您更多的控制权。如果您想快速启动并运行,或者当您使用大型API并希望在整个过程中实施一致的URL配置,则ViewSets很有帮助。

6.10.1ViewSet操作

REST框架随附的默认路由器将为一组标准的创建/检索/更新/销毁样式操作提供路由,如下所示:

class UserViewSet(viewsets.ViewSet):
    """
    Example empty viewset demonstrating the standard
    actions that will be handled by a router class.

    If you're using format suffixes, make sure to also include
    the `format=None` keyword argument for each action.
    """

    def list(self, request):
        pass

    def create(self, request):
        pass

    def retrieve(self, request, pk=None):
        pass

    def update(self, request, pk=None):
        pass

    def partial_update(self, request, pk=None):
        pass

    def destroy(self, request, pk=None):
        pass

内省ViewSet动作

在分发期间,可以使用以下属性ViewSet。

  • basename -用于创建的URL名称的基础。
  • action-当前动作的名称(例如list,create)。
  • detail -布尔值,指示是否为列表视图或详细信息视图配置了当前操作。
  • suffix-视图集类型的显示后缀-镜像detail属性。
  • name-视图集的显示名称。此参数与互斥suffix。
  • description -视图集单个视图的显示描述。

您可以检查这些属性以根据当前操作调整行为。例如,您可以将权限限制为除以下list类似操作外的所有权限:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值