Django一页通-续

视图函数View

FBV - function based view

CBV - class based view

views.py

class MyView(View):
    # 默认支持options方法,作用是查询当前框架支持哪些请求方法。
    # 最多支持这些方法:get、post、put、patch、delete、head、options、trace
    # 其他7种方法需要手写

    info=None # urls.py路由中的参数要提前在类中定义

    # 支持get就支持head
    def get(self,request):
        return HttpResponse(f'get方法,{self.info}')

    def post(self,request):
        # 需要注释掉settings中的csrf中间件代码
        return HttpResponse('post方法')

    def put(self,request):
        return HttpResponse('put方法')

    def patch(self,request):
        return HttpResponse('patch方法')

    def delete(self,request):
        return HttpResponse('delete方法')

    def head(self,request):
        # head方法只传头信息,这些消息体收不到。
        return HttpResponse('head方法,自定义,不用get的方法')

    # def options(self,request):
    #     return HttpResponse('自定义options方法')

    # postman里无trace方法
    def trace(self,request):
        return HttpResponse('trace方法')

    # Django中不支持copy方法
    def copy(self,request):
        return HttpResponse('postman里有的copy方法')

urls.py

    # 相同路由,写在前面的有效,后面的不起作用。
    path('myview/',views.MyView.as_view()), # 类中定义的参数路由中也可以不传,但不能传未定义的参数。
    path('myview/',views.MyView.as_view(info='今天是个好天气')), # 传参必须提前在类为定义

用类视图实现登录

views.py

class Login(View):
    def get(self,request):
        return render(request,'login.html',locals())

    def post(self,request):
        username=request.POST.get('username')
        password=request.POST.get('password')
        return HttpResponse(f'登录成功,用户名:{username},密码:{password}')

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<form action="/myapp/login/" method="post">
    {% csrf_token %}
    <input type="text" placeholder="请输入账号" name="username">
    <input type="password" placeholder="请输入密码" name="password">
    <button>登录</button>
</form>
</body>
</html>

urls.py  path('login/',views.Login.as_view()), 


TemplateView,以下演示代码只能get请求

使用方式一

views.py

class Login(TemplateView):
    template_name = 'login.html'

urls.py  path('login/',views.Login.as_view()), 

使用方式二

views.py

class Login(TemplateView):
    pass

urls.py  path('login/',views.Login.as_view(template_name='login.html')), 

login.html代码略,同上。


ListView

models.py

class Notice(models.Model):
    info=models.CharField(max_length=16)

urls.py  path('notice/',views.List.as_view()), 

方式一

view.py

class List(ListView):
    template_name = 'notice.html'
    model = Notice

notice.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>列表清单</title>
</head>
<body>
    <ul>
        {% for notice in notice_list %} {# 写法一 #}
            <li>{{ notice.info }}</li>
        {% endfor %}
    </ul>
</body>
</html>

方式二

views.py

class List(ListView):
    model = Notice

/mypro/templates/myapp/notice_list.html 自动获取的命名规范:模板目录/应用名/模型名小写_list.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>自动获取列表</title>
</head>
<body>
    <ul>
        {% for notice in object_list %} {# 写法二 #}
            <li>{{ notice.info }}</li>
        {% endfor %}
    </ul>
</body>
</html>

静态资源

mypro/static/home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    静态资源
    {# 和模板html的区别:不支持模板语法 #}
</body>
</html>

配置settings.py

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

然后可直接在浏览器访问:http://127.0.0.1:8000/static/home.html


静态资源,css举例:

urls.py  path('home/',views.home), 

views.py

def home(request):
    return render(request,'home.html')

settings.py

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

mypro/static/css/home.css

h1{
    background-color: red;
}

mypro/templates/home.html ,html模板中含有模板语法,只能放到templates目录中,用视图函数或视图类方法调用。

{% load static %} {# 写法二:相对路径 #}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
    {# 写法一:绝对路径 #}
<!--    <link rel="stylesheet" href="/static/css/home.css">-->
    {# 写法二:相对路径 #}
    <link rel="stylesheet" href="{% static 'css/home.css' %}">
</head>
<body>
<h1>静态资源</h1>
    {# 和模板html的区别:不支持模板语法 #}
</body>
</html>

面向切面编程,AOP,aspect oriented programming

红色加粗为可切入点:

浏览器 -> process_request -> urls路由 -> process_view views视图函数/类方法 -> process_template_response -> templates -> process_response -> 浏览器

其中:views -> models -> views -> templates 不可切。

 mypro/middleware/myMiddleware.py

from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin


class MyMiddleware(MiddlewareMixin):
    def process_request(self,request):
        print('process_request:在执行视图前被调用,每个请求都会调用,不主动返回或返回HttpResponse对象')

    def process_view(self,request,view_func,view_args,view_kwargs):
        print('process_view:在执行视图前被调用,每个请求都会调用,不主动返回或返回HttpResponse对象')

    def process_template_response(self,request,response):
        # 实验无效果,未打印如下信息
        print('在执行完视图后调用,每个请求都会调用,不主动返回或返回HttpResponse对象')

    # def process_response(self,request,response):
        # 实验异常,浏览器显示:A server error occurred.  Please contact the administrator.
        # print('响应浏览器前调用,每个请求都会调用,不主动返回或返回HttpResponse对象')

    def process_exception(self,request,response):
        print('视图异常时调用,不主动返回或返回HttpResponse对象')
        return HttpResponse('视图异常时的返回')

settings.py

MIDDLEWARE = [
    'middleware.myMiddleware.MyMiddleware',
    ……    
]

AOP应用,频率控制

mypro/middleware/myMiddleware.py

from time import time
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin
from myapp.models import AccessLog

class MyMiddleware(MiddlewareMixin):
    def process_request(self,request):
        ip=request.META.get('REMOTE_ADDR')
        accessLogs=AccessLog.objects.filter(ip=ip)if request.path=='/myapp/home/':
            if accessLogs.exists():
                accessLog=accessLogs.last()
                if time()-accessLog.time<=10:
                    return HttpResponse('访问频繁,请10秒后再访问。')
            else:
                accessLog=AccessLog()
                accessLog.ip=ip
            accessLog.time=time()
            accessLog.save()

models.py

class AccessLog(models.Model):
    ip=models.CharField(max_length=16)
    time=models.FloatField()

文件上传,方式一:此方式上传同名文件到同一个目录会直接覆盖,应用场景:修改用户图像,直接将用户图像上传到同户图像目录,每次修改直接覆盖前一个。

views.py

class Upload(TemplateView):
    template_name = 'upload.html' # 写了此行后就默认支持get请求
    def post(self,request):
        # <MultiValueDict: {'mypic': [<InMemoryUploadedFile: 我的图片.png (image/png)>], 'myexcel': [<InMemoryUploadedFile: 我的表格.xlsx (application/vnd.openxmlformats-officedocument.spreadsheetml.sheet)>]}>
        print('request.FILES=',request.FILES)
        
        mypic=request.FILES.get('mypic')
        print('mypic=',mypic) # 显示【文件名】,实际为一个文件对象
        print('type(mypic)=',type(mypic)) # <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>,多个文件为<class 'NoneType'>

        myexcel=request.FILES.get('myexcel')
        print(type(myexcel)) # <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
        print(myexcel) # 显示【文件名】,实际为一个文件对象
        print(myexcel.name) # 真正的文件名
        print(type(myexcel.name)) # <class 'str'>

        pic_path=os.path.join(BASE_DIR,f'static/pics/{mypic.name}')
        excel_path=os.path.join(BASE_DIR,f'static/excels/{myexcel.name}')

        with open(pic_path,'wb') as f:
            for part in mypic.chunks():
                f.write(part)
                f.flush()

        with open(excel_path,'wb') as f:
            for part in myexcel.chunks():
                f.write(part)
                f.flush()

        return HttpResponse(f'上传成功:{request.FILES}')

upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传文件</title>
</head>
<body>
<form action="/myapp/upload/" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <input type="file" name="mypic"> {# 一定要写name,否则上传不了 #}
    <input type="file" name="myexcel">
    <button>上传</button>
</form>
</body>
</html>

urls.py path('upload/',views.Upload.as_view()), 


文件上传,方式二

models.py

class Files(models.Model):
    # 上传图片在生成迁移文件时会提示安装Pillow包,提示中有安装方式
    # 存储的是相对于媒体中心的相对位置路径,同名文件会自动加随机串
    pic=models.ImageField(upload_to='pics/%Y/%m/%d') # 以年月日建子目录
    excel=models.FileField(upload_to='excels/%H/%M/%S') # 以时分秒建子目录

views.py

class Upload(TemplateView):
    template_name = 'upload.html' # 写了此行后就默认支持get请求
    def post(self,request):
        mypic=request.FILES.get('mypic')
        myexcel=request.FILES.get('myexcel')
        files=Files()
        files.pic=mypic
        files.excel=myexcel
        files.save()
        return HttpResponse(f'上传成功:{mypic.name}、{myexcel.name}')

urls.py  path('upload/',views.Upload.as_view()), 

settings.py MEDIA_ROOT=os.path.join(BASE_DIR,'static/uploadedFiles')  媒体中心。


获取图片

urls.py path('getfiles/',views.getfiles) 

settings.py MEDIA_URL_PREFIX='/static/uploadedFiles/' 

displayPic.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>显示图片</title>
</head>
<body>
<img src="{{ picurl }}" alt="我的图片">
</body>
</html>

views.py

def getfiles(request):
    files=Files.objects.last()
    print('类型=',type(files.pic)) # <class 'django.db.models.fields.files.ImageFieldFile'>
    print(files.pic) # 显示的是表中存储的相对媒体中心的路径,实际是文件对象
    print(files.pic.path) # 文件在工程项目主机上的绝对路径
    print(files.pic.url) # 相对媒体中心的相对路径,即网络路径
    print(files.pic.size) # 文件大小,单位字节

    # picurl=os.path.join(BASE_DIR,'static/uploadedFiles',files.pic.url) # 错误写法,本地绝对路径
    # picurl=os.path.join(BASE_DIR,'static/uploadedFiles','pics/x.png') # 错误写法,本地绝对路径
    # picurl='static/uploadedFiles/pics/x.png' # 错误写法,相对路径
    picurl=MEDIA_URL_PREFIX+files.pic.url # 正确写法,网络路径
    print('picurl=',picurl) # /static/uploadedFiles/pics/%E6%88%91%E7%9A%84%E5%9B%BE%E7%89%87_ytvTw5m.png
    # return HttpResponse(f'获取文件成功:{files.pic},{files.excel}')
    return render(request,'displayPic.html',locals())

生成验证码

urls.py path('generateVerifyCode/',views.generateVerifyCode), 

views.py

def generateVerifyCode(request):
    # 需要安装Pillow
    image=Image.new('RGB',(70,40),(100,200,250)) # 画布,RGB必须大写
    draw=ImageDraw.Draw(image,'RGB') # 画笔,RGB必须大写

    font=ImageFont.truetype(os.path.join(BASE_DIR,'static/fonts/SIMLI.TTF'),30)
    # font=ImageFont.truetype(os.path.join(BASE_DIR,'static/fonts/STCAIYUN.TTF'),30)
    # font=ImageFont.truetype(os.path.join(BASE_DIR,'static/fonts/STXINGKA.TTF'),30)

    draw.text((5,5),'ABCD',font=font,fill=(200,0,0)) # 绘制

    buffer=BytesIO() # from io import BytesIO
    image.save(buffer,'png')
    verifyCode=buffer.getvalue()
    return HttpResponse(verifyCode,content_type='image/png')

随机生成并获取验证码

urls.py path('login/',views.login), 

static/js/login.js

$(function () {
    var verifyImg=$("#verifyImg");
    verifyImg.click(function () {
        console.log('获取验证码')
        verifyImg.attr("src","/myapp/generateVerifyCode/?t="+Math.random())
    })
})

login.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
    {# 此jquery脚本不能少,否则js脚本运行异常 #}
    <script type="text/javascript" src="https://cdn.bootcss.com/jquery/1.11.1/jquery.js"></script>
    <script type="text/javascript" src="{% static 'js/login.js' %}"></script>
</head>
<body>
<form action="/myapp/login/" method="post">
    {% csrf_token %}
    <input type="text" placeholder="请输入账号" name="username">
    <input type="password" placeholder="请输入密码" name="password">
    <input type="text" name="verifyCode" placeholder="请输入验证码">
    <img src="/myapp/generateVerifyCode/" id="verifyImg">
    <button>登录</button>
</form>
</body>
</html>

views.py

def generateVerifyCode(request):
    # 需要安装Pillow
    image=Image.new('RGB',(100,50),getColor()) # 画布,RGB必须大写
    draw=ImageDraw.Draw(image,'RGB') # 画笔,RGB必须大写

    font=ImageFont.truetype(os.path.join(BASE_DIR,'static/fonts/SIMLI.TTF'),30)
    verifyStr='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    verifyCode=''
    for i in range(4):
        char=random.choice(verifyStr)
        verifyCode+=char
        x=random.randrange(10)+25*i
        draw.text((x,random.randrange(15)),char,font=font,fill=getColor()) # 绘制

    for i in range(100):
        draw.point((random.randrange(100),random.randrange(50)),fill=getColor())

    request.session['verifyCode']=verifyCode

    buffer=BytesIO() # from io import BytesIO
    image.save(buffer,'png')
    verifyCode=buffer.getvalue()
    return HttpResponse(verifyCode,content_type='image/png')

def getColor():
    red=random.randrange(256)
    green=random.randrange(256)
    blue=random.randrange(256)
    return red,green,blue

def login(request):
    if request.method=='GET':
        return render(request,'login.html')
    elif request.method=='POST':
        verifyCode=request.POST.get('verifyCode')
        username=request.POST.get('username')
        password=request.POST.get('password')
        verifyCodeSrc=request.session.get('verifyCode')
        if verifyCode.lower()!=verifyCodeSrc.lower():
            return HttpResponse('验证码错误')
        return HttpResponse('验证码正确')

富文本,RTF,Rich Text Format

pip install django-tinymce

settings.py

INSTALLED_APPS = [
    ……
    'tinymce',
]

TINYMCE_DEFAULT_CONFIG={
    'theme':'advanced',
    'width':'800',
    'height':'600',
}

站点管理

创建超级用户,终端输入: python manage.py createsuperuser  然后根据提示输入用户名、邮箱、密码等等。登录http://127.0.0.1:8000/admin/看效果。

在应用的admin.py中注册:

from myapp.models import Person

admin.site.register(Person) # 关键代码

登录http://127.0.0.1:8000/admin/看效果。

修改后台表(模型)默认展示信息:

class Person(models.Model):
    # 关键代码
    def __str__(self):
        return self.name
    ……

shell用法

命令:

(venv) D:桌面mypro>python manage.py shell
Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from myapp.models import Person
>>> person=Person()
>>> person.name='后台添加的人'
>>> person.save()

站点管理:自定义管理类

models.py

class Person(models.Model):
    def __str__(self):
        return self.name

    # verbose_name为自定义后台管理页面显示的字段(列或属性)
    name = models.CharField(max_length=32,verbose_name='姓名')
    age=models.IntegerField(default=18,verbose_name='年龄')
    sex=models.NullBooleanField(default=False,verbose_name='性别')
    info=models.TextField(default='',verbose_name='个人信息')
    isStudent=models.BooleanField(default=False,verbose_name='是否学生')
    birth=models.DateTimeField(auto_now=True,verbose_name='生日')

admin.py

from django.contrib import admin

# Register your models here.
from myapp.models import Person

# 创建管理类
class PersonAdmin(admin.ModelAdmin):
    def gender(self):
        if self.sex:
            return ''
        elif self.sex==False:
            return ''
        else:
            return '保密'

    gender.short_description = '性别'

    # 显示规则
    # 显示字段
    list_display = 'name','age',gender,'info','isStudent','birth'
    # 过滤字段,不能像显示字段那样使用方法名,如gender
    list_filter = 'info','isStudent'
    # 搜索字段
    search_fields = 'name','age','sex'
    # 分页,默认每页100条数据
    list_per_page = 10
    # 排序 ordering
    ordering = 'name','age','sex','info','isStudent','birth'
    # 分组,不可编辑字段不能添加到分组,如birth自动取添加时的日期
    fieldsets=(
        ('基本信息',{'fields':('name','age','sex')}),
        ('其他信息',{'fields':('info','isStudent')}),
    )

    # 修改规则:fields显示的字段,exclude不显示的字段。

# 注册管理类
admin.site.register(Person,PersonAdmin)

站点管理,外键约束,设置新增主表一条记录,必须同时新增n条从表记录

models.py

from django.db import models

class Company(models.Model):
    name=models.CharField(max_length=16,verbose_name='公司名')

class Employee(models.Model):
    name=models.CharField(max_length=16,verbose_name='员工名')
    company=models.ForeignKey(Company,null=True,on_delete=models.SET_NULL)

admin.py

from django.contrib import admin

from myapp.models import Company, Employee

class EmployeeInfo(admin.TabularInline):
    model = Employee
    extra = 1 # 新增公司时至少附带新增一个员工

class CompanyAdmin(admin.ModelAdmin):
    inlines = [EmployeeInfo]

admin.site.register(Company,CompanyAdmin)
admin.site.register(Employee)

站点管理,修改登录界面样式、文字、颜色等

在templates模板目录中创建admin目录

复制Libsite-packagesdjangocontribadmin emplatesadminlogin.html到mypro/templates/admin/目录中,并修改为:

{% extends 'admin/login.html' %}

{% load static %}

{# 修改字体颜色 #}
{% block extrastyle %}
  {{ block.super }}
  <link rel="stylesheet" href="{% static 'css/login.css' %}">
{% endblock %}

{# 修改登录框标题 #}
{% block branding %}
<h1 id="site_name"><a href="" id="site_name_color">我的后台管理</a></h1>
{% endblock %}

mypro/static/css/login.css

#site_name_color{
    color:red !important;
}

自定义站点管理

mypro/myapp/admin.py

from django.contrib import admin

from myapp.models import Company, Employee

class EmployeeInfo(admin.TabularInline):
    model = Employee
    extra = 1 # 新增公司时至少附带新增一个员工

class CompanyAdmin(admin.ModelAdmin):
    inlines = [EmployeeInfo]

# admin.site.register(Company,CompanyAdmin)
# admin.site.register(Employee)

class MySite(admin.AdminSite):
    site_header = '我的自定义站点' # 登录后的头部标题,非浏览器标题
    site_title = '浏览器标题'
    site_url = 'http://sogo.com' # 默认http://127.0.0.1:8000/即/

site=MySite()
site.register(Company,CompanyAdmin)
site.register(Employee)

mypro/urls.py

from django.contrib import admin
from django.urls import path, include

from myapp.admin import site

urlpatterns = [
    # path('admin/', admin.site.urls),
    path('admin/',site.urls),
]

可网上搜其他站点界面插件,更改默认站点管理UI界面样式。


缓存

views.py

from time import sleep
from django.http import HttpResponse
from django.views.decorators.cache import cache_page

@cache_page(30) # 缓存保留多长时间,根据具体业务需求设置
def hi(request):
    sleep(5) # 模拟复杂的业务逻辑,第1次访问需要5秒才能显示页面,从第2次开始,30秒内再次访问秒显页面。
    return HttpResponse('hi')

密码加密、校验:make_password()、verify_password()


debug-toolbar

安装: pip install django-debug-toolbar 

配置:

settings.py

INSTALLED_APPS = [
    ……
    'django.contrib.staticfiles',
    'debug_toolbar',
]

MIDDLEWARE = [
    'debug_toolbar.middleware.DebugToolbarMiddleware', # 置于首行
     ……
]

INTERNAL_IPS=[
    '127.0.0.1',
    # ……
]

mypro/mypro/urls.py

import debug_toolbar

urlpatterns = [
    ……
    path('__debug__/',include(debug_toolbar.urls)),
]

注:只对有模板渲染的页面有效。


发送邮件

配置settings.py

# 不加密,默认端口
EMAIL_HOST='smtp.qq.com'
EMAIL_HOST_USER='1*********0@qq.com'
EMAIL_HOST_PASSWORD='s**************e' # 授权码

views.py

from django.http import HttpResponse
from django.core.mail import send_mail

def send_email(request):
    send_mail('邮件主题','邮件内容','1******0@qq.com',['1*********0@qq.com'])
    return HttpResponse('发送成功')

配置settings.py 加密

# 加密
EMAIL_HOST='smtp.qq.com'
EMAIL_PORT=465
EMAIL_HOST_USER='1*********0@qq.com'
EMAIL_HOST_PASSWORD='s**************e'
EMAIL_USE_SSL=True

RESTfull Framework  pip install djangorestframework 

mypro/myapp/serializers.py

# https://www.django-rest-framework.org/tutorial/quickstart/
from django.contrib.auth.models import User, Group
from rest_framework import serializers


class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model=User
        fields=['url','username','email','groups']

class GroupSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model=Group
        fields=['url','name']

views.py

from django.contrib.auth.models import User, Group
from rest_framework import viewsets
from myapp.serializers import UserSerializer, GroupSerializer


class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

class GroupViewSet(viewsets.ModelViewSet):
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

settings.py

INSTALLED_APPS = [
    ……
    'rest_framework',
]

mypro/myapp/urls.py

from rest_framework import routers
from myapp.views import UserViewSet, GroupViewSet

router=routers.DefaultRouter()
router.register('users',UserViewSet)
router.register('groups',GroupViewSet)

mypro/mypro/urls.py

……
from myapp.urls import router

urlpatterns = [
    path('',include(router.urls)),
    ……
]

RESTfull Framework 简明示例二

models.py

class Person(models.Model):
    name=models.CharField(max_length=16)
    age=models.IntegerField(default=18)

serializers.py

class PersonSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model=Person
        fields=('url','name','age')

views.py

class PersonViewSet(viewsets.ModelViewSet):
    queryset = Person.objects.all()
    serializer_class = PersonSerializer

urls.py

router=routers.DefaultRouter()
router.register('persons',PersonViewSet)

mypro/mypro/urls.py

urlpatterns = [
    path('',include(router.urls)),
    ……
]

请求方法PUT必提供全量更新属性值,除非有该字段有默认值。请求url如http://127.0.0.1:8000/persons/2/

请求方法PATCH差量更新某个或某些属性值。请求url如http://127.0.0.1:8000/persons/2/

请求方法POST新增一条记录。请求url如http://127.0.0.1:8000/persons/

请求方法DELETE删除一条记录。请求url如http://127.0.0.1:8000/persons/2/


Serializer

models.py

LANGS=[(0,'Python'),(1,'易语言')]

class Person(models.Model):
    name=models.CharField(max_length=16)
    age=models.IntegerField(default=18)
    lang=models.CharField(choices=LANGS,default='Python',max_length=16)

serializers.py

class PersonSerializer(serializers.Serializer):
    id=serializers.IntegerField(read_only=True)
    name=serializers.CharField(max_length=16)
    age=serializers.IntegerField(default=18)
    lang = serializers.CharField(default='Python', max_length=16)

    def create(self, validated_data):
        return Person.objects.create(**validated_data)

    def update(self, instance, validated_data):
        instance.name=validated_data.get('name',instance.name)
        instance.age=validated_data.get('age',instance.age)
        instance.lang=validated_data.get('lang') or instance.lang
        instance.save()
        return instance

实际开发常用方式

# 用此方式若不带'url'也可以直接访问,
# 若带'url'则需在views函数中带context={'request':request},
# serializer=PersonSerializer(persons,many=True,context={'request':request})
# 且需要要配好单个模型的获取方式,实际开发中用的少。
# class PersonSerializer(serializers.HyperlinkedModelSerializer):

# 实际开发中用此方式最多
class PersonSerializer(serializers.ModelSerializer):
    class Meta:
        model=Person
        fields=('id','name','age','lang')

views.py

def get_person(request):
    person=Person.objects.first()
    serializer=PersonSerializer(person)
    return JsonResponse(serializer.data)

def get_person(request):
    person=Person.objects.first()
    serializer=PersonSerializer(person)
    data={
        'msg':'ok',
        'status':0,
        'data':serializer.data
    }
    return JsonResponse(data)

urls.py

urlpatterns = [
    path('getperson/',views.get_person),
]

mypro/mypro/urls.py

urlpatterns = [
    path('myapp/',include('myapp.urls')),
]

views.py

def add_person(request):
    # 注释掉settings.py中的csrf中间件,在Postman中直接调接口添加Person
    name=request.POST.get('name')
    age=request.POST.get('age')
    lang=request.POST.get('lang')

    person=Person()
    person.name=name
    person.age=age
    person.lang=lang
    person.save()

    serializer=PersonSerializer(person)

    data={
        'msg':'ok',
        'status':0,
        'data':serializer.data
    }
    return JsonResponse(data)

def add_person(request):
    # 注释掉settings.py中的csrf中间件,在Postman中直接调接口添加Person
    name=request.POST.get('name')
    age=request.POST.get('age')
    lang=request.POST.get('lang')

    person_data={
        'name':name,
        'age':age, # 或 'status':status.HTTP_201_CREATED,
        'lang':lang,
    }
    serializer=PersonSerializer(data=person_data)
    if not serializer.is_valid():
        return JsonResponse(serializer.errors)
    serializer.save() # 调用save前必须先调用is_valid()

    data={
        'msg':'ok',
        'status':0,
        'data':serializer.data
    }
    return JsonResponse(data)

urls.py

urlpatterns = [
    path('addperson/',views.add_person),
    path('getperson/',views.get_person),
    ……

查询多个记录

views.py

def get_persons(request):
    persons=Person.objects.all()
    serializer=PersonSerializer(persons,many=True) # 对集合序列化必须指明many=True

    data = {
        'msg': 'ok',
        'status': 0,
        'data': serializer.data
    }
    return JsonResponse(data)

urls.py

urlpatterns = [
    path('getpersons/',views.get_persons),
    path('addperson/',views.add_person),
    path('getperson/',views.get_person),
    ……

api_view装饰器

views.py

@api_view(['GET','POST','PUT','PATCH']) # 加api_view装饰器后只能以指定的请求方式访问,若不加则可以以任何方式访问。
def index(request):
    print(request)
    print(type(request))
    print(request.data) # 通过装饰器@api_view能获取PATCH请求参数
    # return HttpResponse('index')
    return Response('index') # 自动根据请求客户端Accept决定响应Content-Type内容形式

urls.py

urlpatterns = [
    path('index/',views.index),
]

APIView

views.py

class HelloAPIView(APIView):
    def get(self,request):
        data={
            'msg':'ok'
        }
        # return Response(data)
        raise APIException(detail='待需求澄清')

urls.py

urlpatterns = [
    path('hello/',views.HelloAPIView.as_view()),
]

处理多个记录:

views.py

class PersonsAPIView(APIView):
    def get(self,request):
        persons=Person.objects.all()
        serializer=PersonSerializer(persons,many=True)
        return Response(serializer.data)

    def post(self,request):
        name=request.data.get('name')
        age=request.data.get('age') # 不写就可以用模型定义的默认值,写了就必须传。

        person_data={
            'name':name,
            'age':age,
        }

        serializer=PersonSerializer(data=person_data)
        if not serializer.is_valid():
            return Response(serializer.errors)
        serializer.save()
        return Response(serializer.data)

urls.py

urlpatterns = [
    path('persons/',views.PersonsAPIView.as_view()),
]

 处理单个记录:

views.py

class PersonAPIView(APIView):
    def get_person(self,id):
        try:
            person=Person.objects.get(pk=id)
            return person
        except Exception as e:
            raise exceptions.NotFound

    def get(self,request,id):
        person=self.get_person(id)
        print('person的类型:',type(person))
        serializer=PersonSerializer(person)
        print('serializer.data的类型:',type(serializer.data)) # dict的子类
        return Response(serializer.data) # Response接收一个字典或字典的子类

    def put(self,request,id):
        pass

    def patch(self,request,id):
        pass

    def delete(self,request,id):
        person=self.get_person(id)
        person.delete()
        data={
            'msg':'删除成功',
            'status':status.HTTP_204_NO_CONTENT
        }
        return Response(data)

urls.py

urlpatterns = [
    path('persons/<int:id>/',views.PersonAPIView.as_view()), # 建议遵守规范用复数persons
]

GenericAPIView

models.py

class Blog(models.Model):
    title=models.CharField(max_length=16)
    content=models.CharField(max_length=256)

serializers.py

class BlogSerializer(serializers.ModelSerializer):
    class Meta:
        model=Blog
        fields=('id','title','content')

views.py

class BlogsAPIView(GenericAPIView):
    queryset=Blog.objects.all()
    serializer_class=BlogSerializer

    def get(self,request):
        queryset=self.get_queryset()
        serializer=self.get_serializer(queryset,many=True)
        return Response(serializer.data)

    def post(self,request):
        serializer=self.get_serializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors)

class BlogAPIView(GenericAPIView):
    # queryset=Blog.objects.all()
    # serializer_class=BlogSerializer

    def get_blog(self, id):
        try:
            blog = Blog.objects.get(pk=id)
            return blog
        except Exception as e:
            raise exceptions.NotFound

    def get(self, request, id):
        blog = self.get_blog(id)
        serializer = BlogSerializer(blog)
        return Response(serializer.data)  # Response接收一个字典或字典的子类

    def put(self, request, id):
        pass

    def patch(self, request, id):
        pass

    def delete(self, request, id):
        blog = self.get_blog(id)
        blog.delete()
        data = {
            'msg': '删除成功',
            'status': status.HTTP_204_NO_CONTENT
        }
        return Response(data)

urls.py

urlpatterns = [
    path('blogs/<int:id>/',views.BlogAPIView.as_view()),
    path('blogs/',views.BlogsAPIView.as_view()),
]
原文地址:https://www.cnblogs.com/xiongjiawei/p/13768344.html