创建新项目

  1. 新建DjangoREST项目
  2. 配置数据库驱动

    • App中__init__.py:

      import pymysql
      
      pymysql.install_as_MySQLdb()
  3. settings.py
  4. 允许所有主机访问:

    ALLOWED_HOSTS = ['*']
  • 数据库设置

    DATABASES = {
      'default': {
          'ENGINE': 'django.db.backends.mysql',
          'NAME': 'DjangoREST',
          'USER': 'root',
          'PASSWORD': '3825447403',
          'HOST': 'localhost',
          'PORT': 3306,
      }
    }
  • 注册restframework

    INSTALLED_APPS = [
      'django.contrib.admin',
      'django.contrib.auth',
      'django.contrib.contenttypes',
      'django.contrib.sessions',
      'django.contrib.messages',
      'django.contrib.staticfiles',
      'rest_framework',
      'App.apps.AppConfig',
    ]
  1. 创建对应数据库
  2. 迁移数据库

REST中的类视图

官方中文文档:https://q1mi.github.io/Django-REST-framework-documentation/tutorial/3-class-based-views_zh/
可以使用基于类的视图编写我们的API视图,而不是基于函数的视图。我们将看到这是一个强大的模式,允许我们重用常用功能,提高代码复用率,并帮助我们保持代码DRY(整洁)。

如下图,APIView有如下的子类

这些子类都在generics包中。

GenericAPIView

这个是APIView的一个子类,增加了模型的获取操作

get_queryset

获取查询结果集

    def get_queryset(self):
        """
        Get the list of items for this view.
        This must be an iterable, and may be a queryset.
        Defaults to using `self.queryset`.

        This method should always be used rather than accessing `self.queryset`
        directly, as `self.queryset` gets evaluated only once, and those results
        are cached for all subsequent requests.

        You may want to override this if you need to provide different
        querysets depending on the incoming request.

        (Eg. return a list of items that is specific to the user)
        """
        assert self.queryset is not None, (
            "'%s' should either include a `queryset` attribute, "
            "or override the `get_queryset()` method."
            % self.__class__.__name__
        )

        queryset = self.queryset
        if isinstance(queryset, QuerySet):
            # Ensure queryset is re-evaluated on each request.
            queryset = queryset.all()
        return queryset

get_object

获取单个对象,默认是查询主键pk:lookup_field = `'pk'`

    def get_object(self):
        """
        Returns the object the view is displaying.

        You may want to override this if you need to provide non-standard
        queryset lookups.  Eg if objects are referenced using multiple
        keyword arguments in the url conf.
        """
        queryset = self.filter_queryset(self.get_queryset())

        # Perform the lookup filtering.
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

        assert lookup_url_kwarg in self.kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg)
        )

        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs)

        # May raise a permission denied
        self.check_object_permissions(self.request, obj)

        return obj

get_serializer

序列化实例

    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        serializer_class = self.get_serializer_class()
        kwargs.setdefault('context', self.get_serializer_context())
        return serializer_class(*args, **kwargs)

get_serializer_class

获取序列化类

    def get_serializer_class(self):
        """
        Return the class to use for the serializer.
        Defaults to using `self.serializer_class`.

        You may want to override this if you need to provide different
        serializations depending on the incoming request.

        (Eg. admins get full serialization, others get basic serialization)
        """
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )

        return self.serializer_class

get_serializer_context

获取上下文内容

    def get_serializer_context(self):
        """
        Extra context provided to the serializer class.
        """
        return {
            'request': self.request,
            'format': self.format_kwarg,
            'view': self
        }

filter_queryset

对queryset进行过滤

    def filter_queryset(self, queryset):
        """
        Given a queryset, filter it with whichever filter backend is in use.

        You are unlikely to want to override this method, although you may need
        to call it either from a list view, or from a custom `get_object`
        method if you want to apply the configured filtering backend to the
        default queryset.
        """
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset

paginator

分页器

@property
    def paginator(self):
        """
        The paginator instance associated with the view, or `None`.
        """
        if not hasattr(self, '_paginator'):
            if self.pagination_class is None:
                self._paginator = None
            else:
                self._paginator = self.pagination_class()
        return self._paginator

paginate_queryset

对结果集进行分页

    def paginate_queryset(self, queryset):
        """
        Return a single page of results, or `None` if pagination is disabled.
        """
        if self.paginator is None:
            return None
        return self.paginator.paginate_queryset(queryset, self.request, view=self)

get_paginated_response

获取分页后的结果

    def get_paginated_response(self, data):
        """
        Return a paginated style `Response` object for the given output data.
        """
        assert self.paginator is not None
        return self.paginator.get_paginated_response(data)

这些方法就是封装的模型的获取操作,也就是对数据的CURD

补充

增删改查都要基于查询,为什么增也要基于查询?
因为当我们添加一条数据时,如果说要保证这条数据之前没有被创建过,需要先查询一次有没有创建,如果已经创建了就不允许再次创建,不能仅仅在前端做限制,还需要在后端添加数据时做查询检查。

比如买电影票:

按道理已经被预约的位置是不能再次被预定,并且页面已经禁止选中已经被预约的座位。
但是如果遇到程序员同行,对前端按钮状态修改。

可以看到,之前的位置已经可以选中

这样就能进行下单,如果不在后端创建订单时进行检查这个座位是否已经被预约,那么就会出现问题,所以创建数据时从业务逻辑严格的情况下是需要进行查询的。


可以看到如果下单,后端是做了防备的。

CreateAPIView

class CreateAPIView(mixins.CreateModelMixin,
                    GenericAPIView):
    """
    Concrete view for creating a model instance.
    """
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

继承自mixins.CreateModelMixinGenericAPIView

这个VIew实现了创建的类视图,实现了post进行创建
**

mixins.CreateModelMixin

是mixins包中的类

class CreateModelMixin:
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)  # 序列化
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        
        # 没有问题,返回数据,创建成功
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    
    
    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}

ListAPIView

列表的类视图

class ListAPIView(mixins.ListModelMixin,
                  GenericAPIView):
    """
    Concrete view for listing a queryset.
    """
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)  # 这个list方法是ListModelMixin中的

继承自mixins.ListModelMixinGenericAPIView

mixins.ListModelMixin

class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())  # 查询

        page = self.paginate_queryset(queryset)  # 分页
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)  # 获取序列化
        return Response(serializer.data)  # 返回json数据

实现了get列表

RetrieveAPIView

查询单个数据的类视图

class RetrieveAPIView(mixins.RetrieveModelMixin,
                      GenericAPIView):
    """
    Concrete view for retrieving a model instance.
    """
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

继承自mixins.RetrieveModelMixinGenericAPIView

mixins.RetrieveModelMixin

class RetrieveModelMixin:
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()  # 获取单个对象
        serializer = self.get_serializer(instance)  # 序列化
        return Response(serializer.data)

实现了get单个数据,并且序列化

DestroyAPIView

删除数据的类视图

class DestroyAPIView(mixins.DestroyModelMixin,
                     GenericAPIView):
    """
    Concrete view for deleting a model instance.
    """
    def delete(self, request, *args, **kwargs):  # 实现了delete方法
        return self.destroy(request, *args, **kwargs)

继承自mixins.DestroyModelMixinGenericAPIView

DestroyModelMixin

class DestroyModelMixin:
    """
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()  # 获取对象
        self.perform_destroy(instance)  # 执行删除
        return Response(status=status.HTTP_204_NO_CONTENT)  # 返回状态码

    def perform_destroy(self, instance):  # 如果只需要逻辑删除,可以重写下面的代码
        instance.delete()

UpdateAPIView

更新数据的类视图

class UpdateAPIView(mixins.UpdateModelMixin,
                    GenericAPIView):
    """
    Concrete view for updating a model instance.
    """
    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

继承自mixins.UpdateModelMixin和 GenericAPIView
实现了PUT,PATCH请求。

mixins.UpdateModelMixin

class UpdateModelMixin:
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()  # 获取实例
        serializer = self.get_serializer(instance, data=request.data, partial=partial)  # 序列化
        serializer.is_valid(raise_exception=True)  # 进行合法验证
        self.perform_update(serializer)  # 执行更新

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)
    
    def perform_update(self, serializer):  # 对应put方法
        serializer.save()

       def partial_update(self, request, *args, **kwargs):  # 差量跟新,对应patch方法
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

ListCreateAPIView

获取列表数据,创建数据的类视图

class ListCreateAPIView(mixins.ListModelMixin,
                        mixins.CreateModelMixin,
                        GenericAPIView):
    """
    Concrete view for listing a queryset or creating a model instance.
    """
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

继承自mixins.ListModelMixinmixins.CreateModelMixinGenericAPIView
实现了get,post方法.

RetrieveUpdateAPIView

获取单个对象,并且更新单个数据的类视图

class RetrieveUpdateAPIView(mixins.RetrieveModelMixin,
                            mixins.UpdateModelMixin,
                            GenericAPIView):
    """
    Concrete view for retrieving, updating a model instance.
    """
    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 patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

继承自mixins.RetrieveModelMixinmixins.UpdateModelMixinGenericAPIView
实现了get,put,patch

RetrieveDestroyAPIView

获取单个数据,删除单个数据

class RetrieveDestroyAPIView(mixins.RetrieveModelMixin,
                             mixins.DestroyModelMixin,
                             GenericAPIView):
    """
    Concrete view for retrieving or deleting a model instance.
    """
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

继承自mixins.RetrieveModelMixinmixins.DestroyModelMixinGenericAPIView

RetrieveUpdateDestroyAPIView

获取单个数据,更新单个数据,删除单个数据的类视图

class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
                                   mixins.UpdateModelMixin,
                                   mixins.DestroyModelMixin,
                                   GenericAPIView):
    """
    Concrete view for retrieving, updating or deleting a model instance.
    """
    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 patch(self, request, *args, **kwargs):
        return self.partial_update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

继承自:

  • mixins.RetrieveModelMixin
  • mixins.UpdateModelMixin
  • mixins.DestroyModelMixin
  • GenericAPIView

实现了get,put,delete

实践运用类视图

新建models

class  Game(models.Model):
    g_name = models.CharField(max_length=32)
    g_price = models.FloatField(default=0)

然后迁移数据库

进行序列化

新建serializers.py

from rest_framework import  serializers

from App.models import Game


class GameSerializer(serializers.HyperlinkedModelSerializer):
    
    class Meta:
        model = Game
        fields = ['url','g_name','g_price']

views

views.py:

from django.shortcuts import render

from rest_framework.generics import ListCreateAPIView

from App.models import Game
from App.serializers import GameSerializer

# 创建数据视图
class GamesView(ListCreateAPIView):
    # 实例化serializers.py中的序列化类
    serializer_class = GameSerializer
    
    queryset = Game.objects.all()

# 查询单个数据视图
class GameView(RetrieveUpdateDestroyAPIView):

    serializer_class = GameSerializer

    queryset = Game.objects.all()

urls

新建子urls.py

from django.urls import re_path

from App import views

urlpatterns = [
    re_path(r'games/$', views.GamesView.as_view()),
    re_path(r'games/(?P<pk>\d+)', views.GameView.as_view(), name='game-detail'),
]

因为使用serializers.HyperlinkedModelSerializer 这个序列化器,当创建完之后会自动查询刚刚创建的数据,所以需要第二条url:name='game-detail',否则会报没有这个detail的错误。

在总urls中注册

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app/', include('App.urls')),
]

页面测试



可以使用post创建数据,然后使用get获取刚刚创建的数据



可以使用put修改已经创建的数据,也可以使用delete删除

从测试可以看到CRUD功能已经可以实现了,但是这样测试只能方便开发人员测试,并不适用于开发环境。

postman测试

get

查询已有数据

post

创建新数据

补充

差量更新patch和整体更新put

  • put需要提供所有参数才能修改,如果model中该字段设置了默认值default,这个字段也可以不提供,如果说即没有设置默认值,也没有提供参数并且使用put,那么就会提示this field is required。
  • 而差量更新只需要提供需要修改的那部分参数

总结

如果现在需要写增只需要使用create视图,查只需要使用retrieve视图,如果需要查询所有视图,使用list视图。

最后修改:2024 年 03 月 13 日
如果觉得我的文章对你有用,请随意赞赏