视图函数,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()), ]