Django-DRF(ModelViewSet)及自定义函数的使用

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)

urls.py

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']
相关推荐
小咕聊编程1 小时前
【含文档+源码】基于SpringBoot的过滤协同算法之网上服装商城设计与实现
java·spring boot·后端
追逐时光者7 小时前
推荐 12 款开源美观、简单易用的 WPF UI 控件库,让 WPF 应用界面焕然一新!
后端·.net
Jagger_7 小时前
敏捷开发流程-精简版
前端·后端
苏打水com8 小时前
数据库进阶实战:从性能优化到分布式架构的核心突破
数据库·后端
间彧9 小时前
Spring Cloud Gateway与Kong或Nginx等API网关相比有哪些优劣势?
后端
间彧9 小时前
如何基于Spring Cloud Gateway实现灰度发布的具体配置示例?
后端
间彧9 小时前
在实际项目中如何设计一个高可用的Spring Cloud Gateway集群?
后端
间彧9 小时前
如何为Spring Cloud Gateway配置具体的负载均衡策略?
后端
间彧9 小时前
Spring Cloud Gateway详解与应用实战
后端
EnCi Zheng10 小时前
SpringBoot 配置文件完全指南-从入门到精通
java·spring boot·后端