一、初步搭建项目
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_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如下:
class SnippetSerializer(serializers.ModelSerializer):
class Meta:
model = Snippet
fields = ['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 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 视图。
- 用于处理基于函数的视图的修饰器。
@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 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,所以精简代码后我们的代码可以是这样的:
使用通用的基于类的视图
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 种添加如下代码:
##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.添加对象级别的权限(教程里面这个是最好一步,但我觉得放在第一步比较好 本身就是为了设置权限):
#创建一个新文件 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表和用户表相关联 ):
##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.添加用户视图:
## 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"
##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 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 = 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
文件应该如下所示:
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.添加分页
我们可以通过稍微修改我们的文件来更改默认列表样式以使用分页。添加以下设置: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
#在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.访问管理后台
# 在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_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
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)
#实体 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']
请求方式:
## 删除
> 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里面)