zoukankan      html  css  js  c++  java
  • 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()),
    ]
  • 相关阅读:
    MyBatis与spring面试题-转载
    122. 买卖股票的最佳时机 II(贪心策略)
    121. 买卖股票的最佳时机
    120. 三角形最小路径和
    236. 二叉树的最近公共祖先(快手面试)
    b,b+树区别
    119. 杨辉三角 II
    118. 杨辉三角
    检查型异常(Checked Exception)与非检查型异常(Unchecked Exception)
    Redis
  • 原文地址:https://www.cnblogs.com/xiongjiawei/p/13768344.html
Copyright © 2011-2022 走看看