一、初步搭建项目
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
简单看看项目的配置
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.安装相关包
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)
from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_stylesLEXERS = [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如下:
class SnippetSerializer(serializers.ModelSerializer):class Meta:model = Snippetfields = ['id', 'title', 'code', 'linenos', 'language', 'style']
当然有教程是使用了SnippetSerializer序列化并存储了几条数据到这个新模型所在的表里面
7.编写我们的视图(View)
在views.py里面编写代码,如下的风格是Forms API 这种风格
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,添加进去(原有的别删)
from django.urls import path
from snippets import viewsurlpatterns = [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 视图。
- 用于处理基于函数的视图的修饰器。
@api_view
- 用于处理基于类的视图的类。
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
完整代码如下:
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请求),好处就是不用像上面那样要写多个分支
class SnippetDetail(APIView):"""检索,更新或删除一个snippet示例。"""def get_object(self, pk):try:return Snippet.objects.get(pk=pk)except Snippet.DoesNotExist:raise Http404def 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,所以精简代码后我们的代码可以是这样的:
使用通用的基于类的视图
REST框架提供了一组已经混合好(mixed-in)的通用视图,我们可以使用它来简化我们的views.py模块。from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import genericsclass SnippetList(generics.ListCreateAPIView):queryset = Snippet.objects.all()serializer_class = SnippetSerializerclass SnippetDetail(generics.RetrieveUpdateDestroyAPIView):queryset = Snippet.objects.all()serializer_class = SnippetSerializer
4.身份认证和权限验证
1、在models.py 里面Snippet 种添加如下代码:
##models.py from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlightowner = 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 HTMLrepresentation of the code snippet."""lexer = get_lexer_by_name(self.language)linenos = 'table' if self.linenos else Falseoptions = {'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.添加对象级别的权限(教程里面这个是最好一步,但我觉得放在第一步比较好 本身就是为了设置权限):
#创建一个新文件 permissions.py,如果有 直接在里面把这些代码站过去from rest_framework import permissionsclass 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表和用户表相关联 ):
##serializers.pyfrom django.contrib.auth.models import Userclass SnippetSerializer(serializers.ModelSerializer):# 注意:请确保同时添加到内部类的字段列表中。'owner',Meta 在查询会映射usernameowner = serializers.ReadOnlyField(source='owner.username')class Meta:model = Snippetfields = ['id', 'title', 'code', 'linenos', 'language', 'style', 'owner'] #这里的owner就是新增的字段class UserSerializer(serializers.ModelSerializer):snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())class Meta:model = Userfields = ['id', 'username', 'snippets'] #这个snipperts是user的映射
4.添加用户视图:
## views.py
from django.contrib.auth.models import User
from snippets.serializers import UserSerializerclass UserList(generics.ListAPIView):queryset = User.objects.all()serializer_class = UserSerializerclass 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"
##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存放的是用户idowner = 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 Falseoptions = {'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 manage.py makemigrations snippets
python manage.py migrate
## 创建一个超级管理员
python manage.py createsuperuser
用户名:administrator
邮箱:1244@qq.com
密码:123456
6.添加url:
path('users/', views.UserList.as_view()),
path('users/<int:pk>/', views.UserDetail.as_view()),
7.将代码段与用户关联
如果我们需要具体业务跟用户绑定,意味着就是只能这个用户操作(增删改),我们可以在操作里面加上这个:
##这个加到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里面)
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 模式
class SnippetSerializer(serializers.HyperlinkedModelSerializer):owner = serializers.ReadOnlyField(source='owner.username')highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')class Meta:model = Snippetfields = ['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 = Userfields = ['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
文件应该如下所示:
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'),#增加用户urlpath('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.添加分页
我们可以通过稍微修改我们的文件来更改默认列表样式以使用分页。添加以下设置:tutorial/settings.pyREST_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 Responseclass UserViewSet(viewsets.ReadOnlyModelViewSet):"""这viewset自动提供“列表”和“检索”操作。"""queryset = User.objects.all()serializer_class = UserSerializerclass SnippetViewSet(viewsets.ModelViewSet):"""这ViewSet自动提供“list”、“create”、“retrieve”,'update'和'destroy'(也就是删除)操作。此外,我们还提供了一个额外的“突出显示”动作。"""queryset = Snippet.objects.all()serializer_class = SnippetSerializerpermission_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
#在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 Home2.将URL添加到URL模式:path('',Home.as_view(),name='Home')
包括另一个URLconf1.导入include()函数:从django.url导入include,path2.将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_patternsfrom snippets.views import api_root, SnippetViewSet, UserViewSetsnippet_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.pyfrom 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] 创建超级管理员和修改密码changepasswordcreatesuperuser[django] 校验 刷新 dump数据 建立project或app checkdumpdataflushloaddatamakemigrationsmigratesqlmigratestartappstartproject[sessions] 清除所有sessionclearsessions[staticfiles] 运行项目等等collectstaticfindstaticrunserver
六、界面汉化与管理后台
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.访问管理后台
# 在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这类的请求
#!/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_settingsclass 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 = Noneif "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)@classmethoddef get_success_headers(self, data):try:return {'Location': str(data[api_settings.URL_FIELD_NAME])}except (TypeError, KeyError):return {}@classmethoddef perform_update(self, serializer):serializer.save()@classmethoddef partial_update(self, request, *args, **kwargs):kwargs['partial'] = Truereturn self.update(request, *args, **kwargs)@classmethoddef perform_destroy(self, instance):instance.delete()
然后在你需要的ViewSet里面改变他的实现类,换成这个CreateRetrieveViewSet
class TBookManagerViewSet(CreateRetrieveViewSet):"""这ViewSet自动提供“list”、“create”、“retrieve”,'update'和'destroy'(也就是删除)操作。"""queryset = TBookManager.objects.all()serializer_class = TBookManagerSerializerpermission_classes = [permissions.NOT]def perform_create(self, serializer):serializer.save(owner=self.request.user)
#实体 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 = TBookfields = ['book_name', 'book_code', 'author', 'push_time', 'owner', 'url']
请求方式:
## 删除
> 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里面)