Django Rest Framework(DRF)框架搭建步骤,包含部分错误解决

一、初步搭建项目

1.使用PyCharm 2021创建Djiango项目,配置如下(假设应用名叫djiango_python)

  • Python (3.6, 3.7, 3.8, 3.9, 3.10, 3.11)==> 当前版本 3.8.6
  • Django (3.0, 3.1, 3.2, 4.0, 4.1, 4.2)==> 当前版本 4.2.8

简单看看项目的配置

python 复制代码
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'snippets.apps.SnippetsConfig',
    "rest_framework"
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

从上面我们可以看到 admin(管理)、auth(权限)、session(会话)、messages(消息)、csrf等等基本的都有了,这时候我们可以把"rest_framework"加入到INSTALLED_APPS里面,如果没有配置当我们在第二章节包装Api视图时可能会出现这样的错误:

2.安装相关包

python 复制代码
pip install djangorestframework  ##必须
pip install pygments
pip install markdown       # 对可浏览API的markdown格式的支持。
pip install django-filter  # Filtering support


以下包是可选的:
PyYAML, uritemplate (5.1+, 3.0.0+) - 架构生成支持。
Markdown (3.0.0+) - Markdown 对可浏览 API 的支持。
Pygments (2.4.0+) - 在 Markdown 处理中添加语法高亮显示。
django-filter (1.0.1+) - 过滤支持.
django-guardian (1.1.1+) - 对象级权限支持.

3.首次同步数据库并创建初始用户

python manage.py migrate

我们还将创建一个使用密码命名的初始用户("admin")。稍后在示例中,我们将以该用户的身份进行身份验证。

python manage.py createsuperuser --username admin --email admin@example.com

4.创建model类-(Model)

python 复制代码
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']

5.接着我们需要为这个模型创建"初始迁移",并首次同步数据库。

python manage.py makemigrations snippets
python manage.py migrate snippets


6.序列化程序

在(应用 djiango_python)项目下创建serializers.py

这个里面的class类名一般是模型类名+Serializer 。例如:SnippetSerializer,简单的demo如下:

python 复制代码
class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ['id', 'title', 'code', 'linenos', 'language', 'style']

当然有教程是使用了SnippetSerializer序列化并存储了几条数据到这个新模型所在的表里面

7.编写我们的视图(View)

在views.py里面编写代码,如下的风格是Forms API 这种风格

python 复制代码
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)

8.视图写好后,需要连接视图,需要在urls里面配置

配置文件为urls.py,添加进去(原有的别删)

python 复制代码
from django.urls import path
from snippets import views

urlpatterns = [
    path('snippets/', views.snippet_list),
    path('snippets/<int:pk>/', views.snippet_detail),
]

由于这个搭建的项目使用SnippetConfig,这边我们就不用在文件中连接根 (wire up the root urlconf )

9.debug模式下报错(can't find 'main' module in '')解决方法:

勾选 "No reload" 选项

二. 进一步了解

1.请求和响应

请求对象 => Request / HttpRequest

请求的数据:request.data

请求方式:request.POST
响应对象:Response/TemplateResponse

Response.status ==>响应状态码 status.HTTP_400_BAD_REQUEST(这就是请求400)

2.包装 API 视图

REST 框架提供了两个包装器,可用于编写 API 视图。

  1. 用于处理基于函数的视图的修饰器。@api_view
  2. 用于处理基于类的视图的类。APIView

这些包装器提供了一些功能,例如确保在视图中接收实例,以及向对象添加上下文以便可以执行内容协商。

包装器还提供行为,例如在适当的时候返回响应,以及处理使用格式错误的输入进行访问时发生的任何异常。

将之前的代码进行整合:

(ps:这里我们操作的是view层 相当于是java/c#的controller层)

需要导入

​ 1.api_view=》from rest_framework.decorators import api_view

​ 2.Response=》from rest_framework.response import Response

​ 3.status => from rest_framework import status

完整代码如下:

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)

从里面看 风格就很像我们现在的(JAVA/C#)通用的Controller层 ,此时我们再来访问一次接口(如下图所示)

如果我们想要这样的格式支持:

​ =>127.0.0.1:8000/snippets/1.json(以json格式进行返回 如果没有在Setting里 INSTALLED_APPS添加 "rest_framework",也可通过这种方式来访问我们的接口,不会报xx.html找不到的错误)

​ ==>http://127.0.0.1:8000/snippets/1.api (这种方式也能访问接口只不过是以api的方式在界面展示,参考Swagger界面)

​ => 127.0.0.1:8000/snippets.json

​ => 127.0.0.1:8000/snippets.abc (当然这个是找不到的,页面不会报错 只会告诉我们没找到)

​ => 127.0.0.1:8000/snippets/2/?format=json

​ => 127.0.0.1:8000/snippets/2/?format=api

我们用这个链接来访问:127.0.0.1:8000/snippets/1.json

此时告诉我们这个详情接口 少了个format参数,我们再我们的接口上都加上这个参数

然后在urls.py里面加上 =》 urlpatterns = format_suffix_patterns(urlpatterns)

再来访问:

3. 精简代码

基于类的视图,我们写个"查改删"的示例,如下面的代码块,像按主键查询(get请求)、修改(put请求)、删除(delete请求),好处就是不用像上面那样要写多个分支

python 复制代码
class SnippetDetail(APIView):
    """
    检索,更新或删除一个snippet示例。
    """
    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)

这时候如果要访问接口,需要对原来的url进行重构(可以看出我们我们需要把这些类作为视图==》 因为都调用了as_view函数):

urlpatterns = [
    path('snippets/', views.SnippetList.as_view()),
    path('snippets/<int:pk>/', views.SnippetDetail.as_view()),
]

下面是mixins的常用api,其实里面已经封装了常用的增删改查的逻辑:

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

//列表
class ListAPIView(mixins.ListModelMixin,
                  GenericAPIView):
    """
    Concrete view for listing a queryset.
    """
    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

//检索 相当按id查询
class RetrieveAPIView(mixins.RetrieveModelMixin,
                      GenericAPIView):
    """
    Concrete view for retrieving a model instance.
    """
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

//删除
class DestroyAPIView(mixins.DestroyModelMixin,
                     GenericAPIView):
    """
    Concrete view for deleting a model instance.
    """
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

//更新
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)

//列表和新增
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)


//检索(如:id查询) 更新  删除
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)

如果我们这个接口可以用通用的,可以继承对应的类,如ListAPIView,所以精简代码后我们的代码可以是这样的:

python 复制代码
使用通用的基于类的视图
REST框架提供了一组已经混合好(mixed-in)的通用视图,我们可以使用它来简化我们的views.py模块。

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import generics

class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

4.身份认证和权限验证

1、在models.py 里面Snippet 种添加如下代码:

python 复制代码
##models.py 

from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight

owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
highlighted = models.TextField()

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)

2.添加对象级别的权限(教程里面这个是最好一步,但我觉得放在第一步比较好 本身就是为了设置权限):

pyt 复制代码
#创建一个新文件 permissions.py,如果有 直接在里面把这些代码站过去

from rest_framework import permissions


class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    自定义权限,只允许对象的所有者对其进行编辑。
    """

    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        # Write permissions are only allowed to the owner of the snippet.
        return obj.owner == request.user

3.添加用户序列化器(此处增加SnippetSerializer里面owner字段并使其只读,这意味着Snippet表和用户表相关联 ):

python 复制代码
##serializers.py

from django.contrib.auth.models import User

class SnippetSerializer(serializers.ModelSerializer):
    # 注意:请确保同时添加到内部类的字段列表中。'owner',Meta 在查询会映射username
    owner = serializers.ReadOnlyField(source='owner.username')

    class Meta:
      model = Snippet
      fields = ['id', 'title', 'code', 'linenos', 'language', 'style', 'owner'] #这里的owner就是新增的字段

class UserSerializer(serializers.ModelSerializer):
    snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())

    class Meta:
        model = User
        fields = ['id', 'username', 'snippets'] #这个snipperts是user的映射
 

4.添加用户视图:

python 复制代码
## views.py
from django.contrib.auth.models import User
from snippets.serializers import UserSerializer

class UserList(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer


class UserDetail(generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

ps: 在序列化是导入User所在包

正确: from django.contrib.auth.models import User

错误: from rest_framework.authtoken.admin import User

使用下方会报错,未注册"admin"

python 复制代码
##add auth lib
from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight

##省略相关字段


class Snippet(models.Model):
    ##省略其他字段

    ## 增加auth_user表(主键)作为外键  这个字段是owner在数据库会映射成owner_id存放的是用户id
    owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
    highlighted = models.TextField()
    
    ## 
    def save(self, *args, **kwargs):
        """
        使用"pygments"库创建高亮显示的HTML代码段的表示形式。
         """
        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)

    class Meta:
       ##省略....
	
 

5.然后我们手动删除 db.sqlite(文件) 和snippets/migrations(文件夹)

python 复制代码
##重新创建(迁移)数据库和表信息
python manage.py makemigrations snippets
python manage.py migrate
## 创建一个超级管理员
python manage.py createsuperuser
用户名:administrator
邮箱:1244@qq.com
密码:123456

6.添加url:

python 复制代码
path('users/', views.UserList.as_view()),
path('users/<int:pk>/', views.UserDetail.as_view()),

7.将代码段与用户关联

如果我们需要具体业务跟用户绑定,意味着就是只能这个用户操作(增删改),我们可以在操作里面加上这个:

python 复制代码
##这个加到SnippetList这个类视图,我们修改实例保存会记录这个用户信息
def perform_create(self, serializer):
    serializer.save(owner=self.request.user)

这时候我们再来访问接口,发现权限没啥用啊 能看到这个删除和修改操作

这时候不用慌,我们把这个行代码加入就行了:permission_classes = [permissions.XX]

放在哪儿呢?这个房子类视图的里面作为一级字段就行了,示例:

class SnippetList(generics.ListCreateAPIView):
    permission_classes = [permissions.IsAuthenticated]

形式如下:

permission_classes = [permissions.NOT] #无权限访问
permission_classes = [permissions.IsAuthenticated] #有权限
permission_classes = [permissions.IsAuthenticatedOrReadOnly]#有权限但是只读状态(做不了修改删除操作)
permission_classes = [permissions.IsAuthenticatedOrReadOnly,IsOwnerOrReadOnly]#有权限但是只读状态(做不了修改删除操作,IsOwnerOrReadOnly是我们最开始定义的只读类)
permission_classes = [permissions.IsAdminUser] #必须管理员权限

登录前(只有只读权限):

登录后:

通过下方的数据库表snipperts_snippet我们可以看到新增highlighted和owner_id两个字段以及两条数据跟auth_user的id进行了关联



5.配置超链接

之前我们访问这个根url(http://127.0.0.1:8000/)会报错,其实这个应该是把里面的url连接放在这个并能以超链接的形式进行跳转如图所示:

那么需要怎么做:

第一步:在view.py 配置(常使用注解的api配置 方便把url加到下面的Response里面)

python 复制代码
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse


@api_view(['GET'])
def api_root(request, format=None):
    return Response({
        'users': reverse('user-list', request=request, format=format),
        'snippets': reverse('snippet-list', request=request, format=format)
    })

##这个就是把我们高亮代码块 渲染出来(当然没有这个也没多大事)
class SnippetHighlight(generics.GenericAPIView):
    queryset = Snippet.objects.all()
    renderer_classes = [renderers.StaticHTMLRenderer]

    def get(self, request, *args, **kwargs):
        snippet = self.get_object()
        return Response(snippet.highlighted)

第二步:替换原有的序列化器 增加highlight,也就是超链接,格式可以设置html或json

增加两个highlight和url,如果字段有的 Meta类的字段里面也需要增加这个字段

url字段与字段的类型相同,只不过它指向的是 url 模式,而不是 url 模式

python 复制代码
class SnippetSerializer(serializers.HyperlinkedModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')
    highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')

    class Meta:
        model = Snippet
        fields = ['url', 'id', 'highlight', 'owner',
                  'title', 'code', 'linenos', 'language', 'style']


class UserSerializer(serializers.HyperlinkedModelSerializer):
    snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True)

    class Meta:
        model = User
        fields = ['url', 'id', 'username', 'snippets']

第三步:确保我们的URL模式被命名

如果我们要使用超链接的API,那么需要确保为我们的URL模式命名。我们来看看我们需要命名的URL模式。

  • 我们API的根路径是指'user-list''snippet-list'
  • 我们的代码片段序列化器包含一个指向'snippet-highlight'的字段。
  • 我们的用户序列化器包含一个指向'snippet-detail'的字段。
  • 我们的代码片段和用户序列化程序包括'url'字段,默认情况下将指向'{model_name}-detail',在这个例子中就是'snippet-detail''user-detail'

将所有这些名称添加到我们的URLconf中后,最终我们的snippets/urls.py文件应该如下所示:

python 复制代码
urlpatterns = [
    path('', views.api_root),
    path('snippets/', views.SnippetList.as_view() ,name='snippet-list'),
    path('snippets/<int:pk>/', views.SnippetDetail.as_view(),name='snippet-detail'),
    path('snippets/<int:pk>/highlight/', views.SnippetHighlight.as_view(), name='snippet-highlight'),
    #增加用户url
    path('users/', views.UserList.as_view(),name='user-list'),
    path('users/<int:pk>/', views.UserDetail.as_view(),name='user-detail'),
]

# 可浏览API的登录和注销视图(一般配置最后,如果没有配置,访问根url时没有登录和注销的按钮)
urlpatterns += [
    path(r'^api-auth/', include('rest_framework.urls',  namespace='rest_framework')),
]

urlpatterns = format_suffix_patterns(urlpatterns)

django\urls\conf.py

其实这个是来自于conf.py里面的_path 函数,对应路由 视图 视图名称(name)以及匹配模式)

6.添加分页

python 复制代码
我们可以通过稍微修改我们的文件来更改默认列表样式以使用分页。添加以下设置:tutorial/settings.py

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10
}

这边页大小我配置1,页码就是2页了,如图

三、ViewSet 和 Routers

我们看虽然我门在第二节里面精简了代码使用类视图的方式继承了mixins里面的一些api接口,但最少需要两个类来对应 查询以及增删改,这里我们再把这两个类合并当一个视图集合里面,也就需要ViewSet

使用ViewSet

第一步替换view.py里面的所有代码:

from snippets.models import Snippet
from snippets.permissions import IsOwnerOrReadOnly
from snippets.serializers import SnippetSerializer, UserSerializer
from django.contrib.auth.models import User
from rest_framework import viewsets
from rest_framework.decorators import api_view
from rest_framework.reverse import reverse
from rest_framework import permissions
from rest_framework import renderers
from rest_framework.decorators import action
from rest_framework.response import Response

class UserViewSet(viewsets.ReadOnlyModelViewSet):
    """
    这viewset自动提供"列表"和"检索"操作。
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer


class SnippetViewSet(viewsets.ModelViewSet):
    """
    这ViewSet自动提供"list"、"create"、"retrieve",'update'和'destroy'(也就是删除)操作。
    此外,我们还提供了一个额外的"突出显示"动作。
    """
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]

    ##原来对应的视图类=》SnippetHighlight
    @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])
    def highlight(self, request, *args, **kwargs):
        snippet = self.get_object()
        return Response(snippet.highlighted)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

第二步:将 ViewSet 显式绑定到 URL

python 复制代码
#在urls.py替换所有代码
"""
djangoProject项目的URL配置。
"urlpatters"列表将URL路由到视图。有关更多信息,请参阅:
https://docs.djangoproject.com/en/4.2/topics/http/urls/
示例:
功能视图
    1.添加导入:从my_app导入视图
    2.将URL添加到URL模式:path('',views.home,name='home')
基于类的视图
    1.添加导入:from other_app.views import Home
    2.将URL添加到URL模式:path('',Home.as_view(),name='Home')
包括另一个URLconf
    1.导入include()函数:从django.url导入include,path
    2.将URL添加到URL模式:path('blog/',include('blog.urls'))
"""
from django.urls import path, include
from rest_framework import renderers
from rest_framework.urlpatterns import format_suffix_patterns

from snippets.views import api_root, SnippetViewSet, UserViewSet

snippet_list = SnippetViewSet.as_view({
    'get': 'list',
    'post': 'create'
})
snippet_detail = SnippetViewSet.as_view({
    'get': 'retrieve',
    'put': 'update',
    'patch': 'partial_update',
    'delete': 'destroy'
})
snippet_highlight = SnippetViewSet.as_view({
    'get': 'highlight'
}, renderer_classes=[renderers.StaticHTMLRenderer])

user_list = UserViewSet.as_view({
    'get': 'list'
})
user_detail = UserViewSet.as_view({
    'get': 'retrieve'
})

urlpatterns = [
    path('', api_root),
    path('snippets/', snippet_list, name='snippet-list'),
    path('snippets/<int:pk>/', snippet_detail, name='snippet-detail'),
    path('snippets/<int:pk>/highlight/', snippet_highlight, name='snippet-highlight'),
    path('users/', user_list, name='user-list'),
    path('users/<int:pk>/', user_detail, name='user-detail')
]
# 可浏览API的登录和注销视图
urlpatterns += [
    path('api-auth/', include('rest_framework.urls',  namespace='rest_framework')),
]

urlpatterns = format_suffix_patterns(urlpatterns)

访问:http://127.0.0.1:8000/,发现是平替原来的接口功能,同时代码少了不少。但是还是不够好,这边配置url很麻烦,需要对应视图的get put等请求以及方法,其实通用的东西是一样的。有没有更简单的方式?这时候我们可以使用路由。

使用路由(Routes)

因为我们使用的是类而不是类,所以我们实际上不需要自己设计 URL 配置。使用类可以自动处理将资源连接到视图和 URL 的约定。我们需要做的就是向路由器注册适当的视图集,然后让它完成剩下的工作。

##snippets/urls.py

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from snippets import views

# Create a router and register our ViewSets with it.
router = DefaultRouter()
router.register(r'snippets', views.SnippetViewSet, basename='snippet')
router.register(r'users', views.UserViewSet, basename='user')

#API URL现在由路由器自动确定
urlpatterns = [
    path('', include(router.urls)),
]

# 可浏览API的登录和注销视图
urlpatterns += [
    path('api-auth/', include('rest_framework.urls',  namespace='rest_framework')),
]

##这边不在需要格式化 url
#urlpatterns = format_suffix_patterns(urlpatterns)

小结

使用 ViewSet 不如单独构建 API 视图明确。因为api我们对请求方式和处理细节掌握的很到位,但视图集却很抽象。

四、根据model迁移数据库

实际每次会增加一些新的model和view

python manage.py makemigrations [项目名称] 
python manage.py migrate [项目名称] 

## 示例 我这个项目是snippets 我创建了TBook这个表
## 执行以下命令后 生成了snippets_tbook这个表
python manage.py makemigrations snippets
python manage.py migrate snippets

五、python manage.py 常用命令

Available subcommands:

[auth] 创建超级管理员和修改密码
    changepassword
    createsuperuser
    
[django] 校验 刷新 dump数据 建立project或app 
    check
    dumpdata
    flush
    loaddata
    makemigrations
    migrate
    sqlmigrate
    startapp
    startproject

[sessions]  清除所有session
    clearsessions

[staticfiles] 运行项目等等
    collectstatic
    findstatic
    runserver

六、界面汉化与管理后台

1.汉化

# 将以下配置覆盖 settings.py 原来的 里面信息 ,如果Django 版本低于2.0 可以把LANGUAGE_CODE设置成 'zh-cn'
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_TZ = FALSE

2.访问管理后台

python 复制代码
# 在urls.py里面增加"path('admin/', admin.site.urls)" 如图所示
urlpatterns = [
    path('admin/', admin.site.urls),
    # 其他配置不变
]

接着我们就可以访问管理后台了:


七、使用Core API(核心api)

Core API是用于描述API的文档规范。它用于提供可用路径的内部表示形式和API公开的可能的交互。它可以用于服务器端或客户端。

当使用服务器端时,coreAPI允许API支持呈现范围广泛的概要或超媒体格式。

当使用客户端时,核心API允许动态驱动的客户端库,它可以与任何公开受支持的概要或超媒体格式的API交互。

基于 coreapi-2.3.3

pip3 install coreapi
pip3 install pyyam

现在我们可以通过在URL配置中包含一个自动生成的概要视图来为API,下面是需要新添加的,不要直接覆盖你原有配置

##snippets/urls.py
#需要导入的包
from rest_framework.schemas import get_schema_view
schema_view = get_schema_view(title='Pastebin API')

# API URL现在由路由器自动确定
urlpatterns = [
    # settings....
    path('schema/', schema_view),
]

访问:http://127.0.0.1:8000/schema/

八、自定义ViewSet

创建一个 custom/viewset.py,代码如下 这个我是让增删改都是post请求,国内你懂的 很少使用delete put patch这类的请求

python 复制代码
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.mixins import CreateModelMixin, UpdateModelMixin, DestroyModelMixin

# basename- 用于创建的 URL 名称的基础。
# action- 当前操作的名称(例如:list create)。
# detail- 布尔值,指示当前操作是否配置为列表或详细信息视图。
# suffix- 视图集类型的显示后缀 - 镜像属性。detail
# name- 视图集的显示名称。此参数与 互斥。suffix
# description- 视图集的单个视图的显示说明。
from rest_framework.settings import api_settings


class CreateRetrieveViewSet(viewsets.ReadOnlyModelViewSet):
    lookup_field = 'pk'

    # , permission_classes=[IsAdminOrIsSelf]

    @action(detail=False, methods=['get', 'post'], name="addItem", description="新增")
    def addItem(self, request, *args, **kwargs):
        return CreateModelMixin.create(self, request, args, kwargs)

    # 需要嵌入详情里面
    @action(detail=True, methods=['post'], name="updateItem", description="通过主键更新")
    def updateItem(self, request, *args, **kwargs):
        return UpdateModelMixin.update(self, request, args, kwargs)

    # 参数"pk"放在url和放在请求体里面都可以更新
    @action(detail=False, methods=['post'], name="updateBypk", description="通过主键更新")
    def updateBypk(self, request, *args, **kwargs):
        self.lookup_field = 'pk'
        if "pk" not in request.data:
            pk = request.query_params.get('pk', None)
            kwargs = {"pk": pk}
            self.kwargs = {"pk": pk}
        else:
            self.kwargs = {"pk": request.data["pk"]}
            kwargs = {"pk": request.data["pk"]}
        return UpdateModelMixin.update(self, request, args, kwargs)

    # 参数"pk"放在url和放在请求体里面都可以删除
    # 如果需要后面携带参数 pk=xx 这种 detail设置为False
    @action(detail=False, methods=['post'], name="deleteItem", description="通过主键删除")
    def deleteBypk(self, request, *args, **kwargs):
        self.lookup_field = 'pk'
        pk = None
        if "pk" not in request.data:
            pk = request.query_params.get('pk', None)
            self.kwargs = {'pk': pk}
        else:
            pk = request.data["pk"]
            self.kwargs = {"pk": request.data["pk"]}
        return DestroyModelMixin.destroy(self, request, pk)

    @action(detail=True, methods=['post'], name="deleteByPathVar", description="通过路径携带id进行删除")
    def deleteItem(self, request, pk=None):
        return DestroyModelMixin.destroy(self, request, pk)

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

    @classmethod
    def perform_update(self, serializer):
        serializer.save()

    @classmethod
    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

    @classmethod
    def perform_destroy(self, instance):
        instance.delete()

然后在你需要的ViewSet里面改变他的实现类,换成这个CreateRetrieveViewSet

python 复制代码
class TBookManagerViewSet(CreateRetrieveViewSet):
    """
       这ViewSet自动提供"list"、"create"、"retrieve",'update'和'destroy'(也就是删除)操作。
       """
    queryset = TBookManager.objects.all()
    serializer_class = TBookManagerSerializer
    permission_classes = [permissions.NOT]

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)
python 复制代码
#实体 model
class TBookManager(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    name = models.TextField()
    entry_time = models.DateTimeField()
    gender = models.CharField(choices=[('man','男'),('woman','女')], default='man', max_length=5)

    ## 增加模型的权限用户
    owner = models.ForeignKey('auth.User', related_name='tbookmanager', on_delete=models.CASCADE)

    class Meta:
        ordering = ['created']
        
#序列化      
class TBookSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')
    
    class Meta:
        model = TBook
        fields = ['book_name', 'book_code', 'author', 'push_time', 'owner', 'url']

请求方式:

python 复制代码
## 删除
> http://localhost:8000/bookmanager/10/deleteItem
> http://localhost:8000/bookmanager/deleteBypk/?pk=9
> http://localhost:8000/bookmanager/deleteBypk/ 
> (参数可放form-data里面)
> ##更新
> http://localhost:8000/bookmanager/9/updateItem
> http://localhost:8000/bookmanager/updateBypk/ ?pk=9
>  http://localhost:8000/bookmanager/updateBypk/ 
>  (参数可放form-data里面)
相关推荐
Mephisto.java几秒前
【大数据学习 | HBASE高级】region split机制和策略
数据库·学习·hbase
赛丽曼5 分钟前
Python中的HTML
python·html
大气层煮月亮9 分钟前
python调用MySql详细步骤
数据库·mysql
luky!13 分钟前
算法--解决熄灯问题
python·算法
Code哈哈笑18 分钟前
【MySQL 保姆级教学】详细讲解视图--(15)
数据库·mysql
深度学习lover27 分钟前
<项目代码>YOLOv8 番茄识别<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·番茄识别
IT古董27 分钟前
【机器学习】机器学习中用到的高等数学知识-1.线性代数 (Linear Algebra)
人工智能·python·线性代数·机器学习
生信与遗传解读31 分钟前
基于python的线性代数相关计算
python·线性代数·机器学习
Py小趴43 分钟前
Python自学之Colormaps指南
开发语言·python·数据可视化
晒足以百八十1 小时前
基于Python 和 pyecharts 制作招聘数据可视化分析大屏
开发语言·python·信息可视化