rest_framework中Serialization、 ModelViewSet学习
1.环境搭建
为了区分之前内容,新建应用snippets,独立操作。
cmd
python manage.py startapp snippets
1.1 model准备
准备了一格5列的数据表
python
from django.db import models
# Create your models here.
from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles
LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
STYLE_CHOICES = sorted([(item, item) for item in get_all_styles()])
class Snippet(models.Model):
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=True, default='')
code = models.TextField()
linenos = models.BooleanField(default=False)
language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
class Meta:
ordering = ['created']
1. 2数据库准备
当然installed_apps种需要加应用名snippets
cmd
python manage.py startapp snippets
python manage.py makemigrations snippets
python manage.py migrate snippets
2.组装
2.1序列化
依葫芦画瓢,调用serializers接口初始化。
python
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
class SnippetSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(required=False, allow_blank=True, max_length=100)
code = serializers.CharField(style={'base_template': 'textarea.html'})
linenos = serializers.BooleanField(required=False)
language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')
def create(self, validated_data):
"""
Create and return a new `Snippet` instance, given the validated data.
"""
return Snippet.objects.create(**validated_data)
def update(self, instance, validated_data):
"""
Update and return an existing `Snippet` instance, given the validated data.
"""
instance.title = validated_data.get('title', instance.title)
instance.code = validated_data.get('code', instance.code)
instance.linenos = validated_data.get('linenos', instance.linenos)
instance.language = validated_data.get('language', instance.language)
instance.style = validated_data.get('style', instance.style)
instance.save()
return instance
2.2 views设置
- 列表函数
- 细节展示函数
python
from django.shortcuts import render
# Create your views here.
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
@csrf_exempt
def snippet_list(request):
"""
List all code snippets, or create a new snippet.
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return JsonResponse(serializer.data, safe=False)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = SnippetSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data, status=201)
return JsonResponse(serializer.errors, status=400)
@csrf_exempt
def snippet_detail(request, pk):
"""
Retrieve, update or delete a code snippet.
"""
try:
snippet = Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
return HttpResponse(status=404)
if request.method == 'GET':
serializer = SnippetSerializer(snippet)
return JsonResponse(serializer.data)
elif request.method == 'PUT':
data = JSONParser().parse(request)
serializer = SnippetSerializer(snippet, data=data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
return JsonResponse(serializer.errors, status=400)
elif request.method == 'DELETE':
snippet.delete()
return HttpResponse(status=204)
2.3 添加urls
可以单独在应用下加urls,然后再引用到project的总urls里,这样层次比较清晰。
javascript
from django.urls import path
from snippets import views
urlpatterns = [
path('snippets/', views.snippet_list),
path('snippets/<int:pk>/', views.snippet_detail),
]
javascript
from django.contrib import admin
from django.urls import path
from app01.views import *
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
# path('filtergoodscategory/', FilterGoodsCategory),
# path('insertgoodscategory/', InsertGoodsCategory),
path("filetgoodscategoryapi/", FilterGoodsCategoryAPI.as_view()),
# path("insertgoodscategory/", InsertGoodsCategory),
path('getgoods/', GetGoods.as_view()),
path('', include('snippets.urls')),
]
2.4 shell调试
cmd
python manage.py shell
#进入后可以进行数据增删改查
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
snippet = Snippet(code='foo = "bar"\n')
snippet.save()
snippet = Snippet(code='print("hello, world")\n')
snippet.save()
serializer = SnippetSerializer(snippet)
serializer.data
content = JSONRenderer().render(serializer.data)
content
import io
stream = io.BytesIO(content)
data = JSONParser().parse(stream)
serializer = SnippetSerializer(data=data)
serializer.is_valid()
serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
2.5 http调试
那个postman登录有时候半天登陆不上去(QTMD外网),用httpie很方便
cmd
pip install httpie
http http://127.0.0.1:8000/snippets/
(py10) PS D:\work> http http://127.0.0.1:8000/snippets/2/
HTTP/1.1 200 OK
Content-Length: 120
Content-Type: application/json
Cross-Origin-Opener-Policy: same-origin
Date: Sun, 22 Oct 2023 01:54:38 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.10.12
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
{
"code": "print(\"hello, world\")\n",
"id": 2,
"language": "python",
"linenos": false,
"style": "friendly",
"title": ""
}
(py10) PS D:\work> http http://127.0.0.1:8000/snippets/
HTTP/1.1 200 OK
Content-Length: 234
Content-Type: application/json
Cross-Origin-Opener-Policy: same-origin
Date: Sun, 22 Oct 2023 02:12:34 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.10.12
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
[
{
"code": "foo = \"bar\"\n",
"id": 1,
"language": "python",
"linenos": false,
"style": "friendly",
"title": ""
},
{
"code": "print(\"hello, world\")\n",
"id": 2,
"language": "python",
"linenos": false,
"style": "friendly",
"title": ""
}
]
3. requests-responses
3.1新建views_new.py
python
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
@api_view(['GET', 'POST'])
def snippet_list(request):
"""
List all code snippets, or create a new snippet.
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@api_view(['GET', 'PUT', 'DELETE'])
def snippet_detail(request, pk):
"""
Retrieve, update or delete a code snippet.
"""
try:
snippet = Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = SnippetSerializer(snippet)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = SnippetSerializer(snippet, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
3.2 urls.py
python
from django.urls import path
from snippets import views_new
urlpatterns = [
path('snippets/', views_new.snippet_list),
path('snippets/<int:pk>/', views_new.snippet_detail),
]
3.3 调试
cmd
(py10) PS D:\work> http http://127.0.0.1:8000/snippets/ Accept:application/json
HTTP/1.1 200 OK
Allow: POST, GET, OPTIONS
Content-Length: 211
Content-Type: application/json
Cross-Origin-Opener-Policy: same-origin
Date: Sun, 22 Oct 2023 02:38:07 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.10.12
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
[
{
"code": "foo = \"bar\"\n",
"id": 1,
"language": "python",
"linenos": false,
"style": "friendly",
"title": ""
},
{
"code": "print(\"hello, world\")\n",
"id": 2,
"language": "python",
"linenos": false,
"style": "friendly",
"title": ""
}
]
或者http http://127.0.0.1:8000/snippets/ Accept:text/html
或者
bash
http http://127.0.0.1:8000/snippets.json # JSON suffix
http http://127.0.0.1:8000/snippets.api # Browsable API suffix
4.class-based views
好处是代码很干,没水份,多余的都给框架弄了。
4.1创建views_new_classbased.py
python
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class SnippetList(APIView):
"""
List all snippets, or create a new snippet.
"""
def get(self, request, format=None):
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class SnippetDetail(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
def get_object(self, pk):
try:
return Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet)
return Response(serializer.data)
def put(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
snippet = self.get_object(pk)
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
4.2 修改urls.py
javascript
from django.urls import path
from snippets import views_new_classbased
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views_new_classbased
urlpatterns = [
path('snippets/', views_new_classbased.SnippetList.as_view()),
path('snippets/<int:pk>/', views_new_classbased.SnippetDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
4.3 使用mixins
views简化了
python
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import mixins
from rest_framework import generics
class SnippetList(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class SnippetDetail(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
python
from django.urls import path
from snippets import views_new_classbased
from rest_framework.urlpatterns import format_suffix_patterns
# from snippets import views_new_classbased
from snippets import views_mixins
urlpatterns = [
path('snippets/', views_mixins.SnippetList.as_view()),
path('snippets/<int:pk>/', views_mixins.SnippetDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
结果并无区别
知识点 | 请求 | url | 特点 |
---|---|---|---|
GenericViewSet | 提供一组通用的视图方法,方便实现特定功能 | ||
ListModelMixin | get | 127.0.0.1:8000/book/ | 提供 list 方法,用于获取资源列表 |
RetrieveModelMixin | get | 127.0.0.1:8000/book/{1}/ | 提供 retrieve 方法,用于获取单个资源的详细信息 |
CreateModelMixin | post | 127.0.0.1:8000/book/ | 提供 create 方法,用于创建资源 |
UpdateModelMixin | put | 127.0.0.1:8000/book/{1}/ | 提供 update 方法,用于更新资源 |
DestroyModelMixin | detete | 127.0.0.1:8000/book/{1}/ | 提供 destroy 方法,用于删除资源 |
自定义 | get/post | 127.0.0.1:8000/book/自定义 | 用户自定义方法/函数 |
5. 鉴权
5.1 model.py
python
from django.db import models
# Create your models here.
from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles
from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight
LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
STYLE_CHOICES = sorted([(item, item) for item in get_all_styles()])
class Snippet(models.Model):
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=True, default='')
code = models.TextField()
linenos = models.BooleanField(default=False)
language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
highlighted = models.TextField()
class Meta:
ordering = ['created']
def save(self, *args, **kwargs):
"""
Use the `pygments` library to create a highlighted HTML
representation of the code snippet.
"""
lexer = get_lexer_by_name(self.language)
linenos = 'table' if self.linenos else False
options = {'title': self.title} if self.title else {}
formatter = HtmlFormatter(style=self.style, linenos=linenos,
full=True, **options)
self.highlighted = highlight(self.code, lexer, formatter)
super().save(*args, **kwargs)
5.2 数据库生成
cmd
del -f db.sqlite3
rmdir -r snippets/migrations
python manage.py makemigrations snippets
python manage.py migrate
python manage.py createsuperuser
5.3 序列号
python
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
from django.contrib.auth.models import User
class SnippetSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(required=False, allow_blank=True, max_length=100)
code = serializers.CharField(style={'base_template': 'textarea.html'})
linenos = serializers.BooleanField(required=False)
language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')
def create(self, validated_data):
"""
Create and return a new `Snippet` instance, given the validated data.
"""
return Snippet.objects.create(**validated_data)
def update(self, instance, validated_data):
"""
Update and return an existing `Snippet` instance, given the validated data.
"""
instance.title = validated_data.get('title', instance.title)
instance.code = validated_data.get('code', instance.code)
instance.linenos = validated_data.get('linenos', instance.linenos)
instance.language = validated_data.get('language', instance.language)
instance.style = validated_data.get('style', instance.style)
instance.save()
return instance
class UserSerializer(serializers.ModelSerializer):
snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())
class Meta:
model = User
fields = ['id', 'username', 'snippets']