如何统一处理DRF的异常?使用custom_exception_handler:
https://stackoverflow.com/questions/28197199/how-to-change-validation-error-responses-in-drf
对于可以为None的值,前台不设置该key即可,可以通过序列化器的数据校验
嵌套序列化传参:
data = {
'up_credit.username': 'root',
'up_credit.password': 'qwe',
'name': 'z10',
'hosts': '192.168.2.111;',
"exclude_hosts": "",
"alive_test": 1,
"port_list": 1,
"comment": "",
'ssh_port': ''
}
参数传递过去后,会被序列化器封装成一个字典,字典包含了被嵌套序列化对象的所有参数。
对于嵌套序列化的数据校验,如果允许None的话,那么up_credit都不传值即可,但如果up_credit.username传值了,password却没传值,那么是会引发报错的。
当POST和PUT方法提交的参数不同的时候,可以使用两个不同的URL和VIEW来提供API:
path('remote-app/create/', views.RemoteAppCreateView.as_view(), name='remote-app-create'),
path('remote-app//update/', views.RemoteAppUpdateView.as_view(), name='remote-app-update'),
serializer实例化传入data,这时data是ReturnDict类型,不可变对象,不能改变其的值。
https://stackoverflow.com/questions/37111118/update-append-serializer-data-in-django-rest-framework
https://www.django-rest-framework.org/api-guide/pagination/#pagenumberpagination # 分页
序列化器会将前台POST的数据转换成models中定义的数据类型,所以对于int型,不论前台传的是int还是str,都会被序列化成int类型。
默认情况下,“一起唯一”验证会强制执行所有字段 required=True
。在某些情况下,您可能希望显式应用于 required=False
其中一个字段,在这种情况下,验证的所需行为是不明确的。
在这种情况下,您通常需要从序列化程序类中排除验证程序,而是在.validate()
方法中或在视图中显式写入任何验证逻辑。
例如:
class BillingRecordSerializer(serializers.ModelSerializer):
def validate(self, data):
# Apply custom validation either here, or in the view.
class Meta:
fields = ('client', 'date', 'amount')
extra_kwargs = {'client': {'required': False}}
validators = [] # Remove a default "unique together" constraint.
关于depth参数(针对ModelSerializer):
使用该参数可以轻松获得外键或多对多关系的具体数据,而不是单单显示一个key,缺点是使用了该参数后无法使用create和update方法,存在外键关系的字段会被过滤掉。
解决方法:重写create和update方法,将使用的serializer指向另一个serializer,一个除了没有指定depth参数,其余皆和原serializer相同的序列化器。
方法1:
class A(models.Model):
name = models.CharField(max_length=255, unique=True)
class B(models.Model):
a = models.ForeignKey(A, on_delete=models.PROTECT)
name = models.CharField(max_length=255, unique=True)
通过A的实例反向查询所有引用过A的B对象:
A.objects.filter(id=1).values_list('blist')
输出QuerySet [None] or [(1,), (2,)]
方法2:
ret = PortList.objects.filter(id=1, targetlist__uniq_id__isnull=False)
返回QuerySet [] or [obj,obj],len(ret)即是id为1的PortList被引用的次数。
获取实例的类名:
obj.__class__.__name__
ForeignKey字段可以设置为null=True,那么允许前端不选择关联对象,而是传递一个null。经过序列化的数据该key对应的value为None
使用ViewSet的情况下,如果再settings中设置了分页,那么默认会对数据进行分页处理。该处理继承于GenericAPIView,在APIView中是没有这样处理的,所以如果使用了APIView,需要手动进行分页,代码如下:
paginate_queryset,可以传入queryset对象或者list等,该方法计算出相关数值,get_paginated_response会将数据进行分页然后返回一个django restframework中的Response对象,不需要额外使用Response对象对其进行封装。
def get(self, request):
data = list()
op_return = open_vas.exec_cmd('get_tasks')
for i in op_return['get_tasks_response']['task']:
data.append({'uniq_id': i['@id'],'status': i['status']})
page = LimitOffsetPagination()
page_roles = page.paginate_queryset(data, self.request, view=self)
result = page.get_paginated_response(page_roles)
return result
在中间件中获取request数据出现的问题:
django request.POST / request.body
当request.POST没有值 需要考虑下面两个要求
1.如果请求头中的: Content-Type: application/x-www-form-urlencoded request.POST中才会有值(才会去request.body中解析数据)
2.若1有,也不一定有值 必须有数据格式要求: name=alex&age=18&gender=男
如果已经读取过request.body的数据,那么不能二次读取
multipart/form-data:既可以上传文件等二进制数据,也可以上传表单键值对,只是最后会转化为一条信息;
x-www-form-urlencoded:只能上传键值对,并且键值对都是间隔分开的。
相关文档:
https://www.django-rest-framework.org/api-guide/filtering/#searchfilter
https://django-filter.readthedocs.io/en/latest/index.html
https://django-filter.readthedocs.io/en/latest/guide/rest_framework.html
DEMO:
STEP 1:下载及加载模块
pip install django-filter
INSTALLED_APPS = [
...
'rest_framework',
'django_filters',
]
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': (
'django_filters.rest_framework.DjangoFilterBackend',
}
STEP 2:编写fiter
from django_filters import rest_framework as filters
from .models import ActionLog
class ActionLogFilter(filters.FilterSet):
comment = filters.CharFilter(lookup_expr='icontains')
class Meta:
model = ActionLog
fields = ['user', 'status', 'dtime', 'comment'] # 若不指定具体查找内容将自动使用精确查找
STEP 3:将filter与ViewSet绑定
class ActionLogViewSet(ModelViewSet):
permission_classes = (permissions.IsAdminUser,)
queryset = ActionLog.objects.all()
serializer_class = ActionLogSerializer
filter_backends = (filters.DjangoFilterBackend,) # !!!
filterset_class = ActionLogFilter # !!!
STEP 4:测试
http://192.168.2.178:8000/logs/action/?user=AnonymousUser
支持使用fields字段来简化代码:
import django_filters
class ProductFilter(django_filters.FilterSet):
class Meta:
model = Product
fields = {
'price': ['lt', 'gt'],
'release_date': ['exact', 'year__gt'],
}
# 以上将生成'price__lt','price__gt','release_date'和'release_date__year__gt'过滤器
class ActionLogFilter(filters.FilterSet):
comment = filters.CharFilter(lookup_expr='icontains')
dtime = filters.DateFilter(lookup_expr='exact')
dtime_range = filters.DateFromToRangeFilter(field_name='dtime')
class Meta:
model = ActionLog
fields = ['user', 'status', 'dtime', 'comment'] # 若不指定具体查找内容将自动使用精确查找
http://192.168.2.178:8000/logs/action/?dtime_range_after=2019-06-18&dtime_range_before=2019-06-17
在settings.py中针对DRF设置了默认分页器的话,那么ModelViewSet视图集会自动进行分页:
REST_FRAMEWORK = {
# 只有使用rest_framework的API才会受到JWT的保护
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
# 使用rest_framework的界面需要下列认证模块
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
# 以下代码会禁用掉REST自带的WEB API界面,只返回JSON
# 'DEFAULT_RENDERER_CLASSES': (
# 'rest_framework.renderers.JSONRenderer',
# ),
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 50,
'DEFAULT_FILTER_BACKENDS': (
'django_filters.rest_framework.DjangoFilterBackend',
),
}
但是APIView不会,需要手动分页,手动分页方法如下:
class TaskStatusGetView(APIView):
permission_classes = (UserReadAdminWrite,)
def get(self, request):
data = list()
op_return = open_vas.exec_cmd('get_tasks')
for i in op_return['get_tasks_response']['task']:
# 如果status为Done,progress为-1
data.append({'uniq_id': i['@id'],'status': i['status'],'progress': i['progress']})
page = LimitOffsetPagination()
page_roles = page.paginate_queryset(data, self.request, view=self)
result = page.get_paginated_response(page_roles)
return result
class BackupLogView(APIView):
permission_classes = (permissions.IsAdminUser,)
def get(self, request):
with open(config['BACKUP']['LINE_PATH'], 'r') as f:
line_num = f.readline()
query_set = ActionLog.objects.filter(id__gt=line_num)
if len(query_set) > 0:
example = 'ID:%s,用户:%s,操作详情:%s,操作状态:%s,时间:%s'
with open(config['BACKUP']['LOG_PATH'], 'a') as syslog:
for i in query_set:
syslog.write(example % (i.id, i.user, i.comment, i.status, i.dtime))
syslog.write('\n')
with open(config['BACKUP']['LINE_PATH'], 'w') as fr:
fr.write(str(query_set[len(query_set)-1].id))
result = {'result': ''}
with open(config['BACKUP']['LOG_PATH'], 'r') as file:
for line in file:
result['result'] = result['result'] + line + '\r\n'
response = HttpResponse(result['result'], content_type='text/plain; charset=UTF-8')
# response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = 'attachment;filename="sys_op_log.txt"'
return response
支持通过binary file或表单来上传文件。如果文件小于2.5MB,那么Django会将数据全部保存到内存中,如果需要对文件进行操作,操作全部在内存执行,非常快。如果文件大于2.5MB,Django会把文件存储到系统目录,然后需要使用的时候将临时文件按块读取。传送门:https://docs.djangoproject.com/en/2.2/topics/http/file-uploads/
from rest_framework.parsers import MultiPartParser
class UpdateNvtView(APIView):
permission_classes = (permissions.IsAdminUser,)
parser_classes = [MultiPartParser]
def put(self, request):
print(request.data)
zip_file = request.data['file']
with open('/home/rule.zip', 'wb') as f:
for chunk in zip_file.chunks():
f.write(chunk)
return Response(status=status.HTTP_200_OK)
字段类型
获取choice字段的描述:
如果设置了choice类型,那么非设置的值不允许传入,传入会报错:
{
"credit_type": [
"“3” 不是合法选项。"
]
}
choice = (
(1, 'up'), # username + password
(2, 'snmp') # snmp community
)
credit_type = models.IntegerField(choices=choice)
# 前台传参需要传递int类型
字段选项
关系
使用ORM创建了表后切忌手动进入数据库删除表信息,如果删除了将导致无法再migrate提交修改到数据库。解决方法:
删除django_migrations表中的提交记录,注意,只需删除自己APP最近的一条提交记录。
on_delete指的是通过ForeignKey连接起来的对象被删除后,当前字段怎么变化。
常见的选项有:
models.CASCADE,对就对象删除后,包含ForeignKey的字段也会被删除
models.PROTECT,删除时会引起ProtectedError
models.SET_NULL,注意只有当当前字段设置null设置为True才有效,此情况会将ForeignKey字段设置为null
models.SET_DEFAULT ,同样,当前字段设置了default才有效,此情况会将ForeignKey 字段设置为default 值
moels.SET,此时需要指定set的值
models.DO_NOTHING ,什么也不做
模型中Meta配置:
对于一些模型级别的配置。我们可以在模型中定义一个类,叫做Meta。然后在这个类中添加一些类属性来控制模型的作用。比如我们想要在数据库映射的时候使用自己指定的表名,而不是使用模型的名称。那么我们可以在Meta类中添加一个db_table的属性。示例代码如下:
class Book(models.Model):
name = models.CharField(max_length=20,null=False)
desc = models.CharField(max_length=100,null=True,blank=True)
class Meta:
db_table = 'book_model'
以下将对Meta类中的一些常用配置进行解释。
1、db_table:这个模型映射到数据库中的表名。如果没有指定这个参数,那么在映射的时候将会使用模型所在app的名称加上模型名的小写来作为默认的表名。
2、ordering:设置在提取数据的排序方式,因为可以按照多个字段以优先关系进行排序,所以需要传递一个字段的列表,在我们提取数据时,可以根据列表中字段从前到后(优先级从高到低)的方式排序,排序默认为正序,如果你需要哪个字段按倒序排列,就可以在这个字段前面加上"-"。后面章节会讲到如何查找数据。比如我想在查找数据的时候根据添加的时间排序,那么示例代码如下:
class Book(models.Model):
name = models.CharField(max_length=20,null=False)
desc = models.CharField(max_length=100,name='description',db_column="description1")
pub_date = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'book_model'
ordering = ['pub_date',]
3、get_latest_by
由于Django的管理方法中有个lastest()方法,就是得到最近一行记录。如果你的数据模型中有 DateField 或 DateTimeField 类型的字段,你可以通过这个选项来指定lastest()是按照哪个字段进行选取的。
一个 DateField 或 DateTimeField 字段的名字. 若提供该选项, 该模块将拥有一个 get_latest() 函数以得到 "最新的" 对象(依据那个字段):
get_latest_by = "order_date"
4、app_label
app_label这个选项只在一种情况下使用,就是你的模型类不在默认的应用程序包下的models.py文件中,这时候你需要指定你这个模型类是那个应用程序的。比如你在其他地方写了一个模型类,而这个模型类是属于myapp的,那么你这是需要指定为:
app_label='myapp'
5、unique_together
unique_together这个选项用于:当你需要通过两个字段保持唯一性时使用。这会在 Django admin 层和数据库层同时做出限制(也就是相关的 UNIQUE 语句会被包括在 CREATE TABLE 语句中)。比如:一个Person的FirstName和LastName两者的组合必须是唯一的,那么需要这样设置:
unique_together = (("first_name", "last_name"),)
使用model层的validators:
https://docs.djangoproject.com/en/2.2/ref/validators/
from django.db import models
from django.core import validators
class Interface(models.Model):
name = models.CharField(max_length=20)
ip = models.GenericIPAddressField(protocol='IPv4')
mask = models.IntegerField(validators=[
validators.MinValueValidator(1),
validators.MaxValueValidator(32),
])
status = models.BooleanField()
class Meta:
db_table = 'net_interface'
增:
models.UserInfo.objects.create(name=new_name)
删:
models.UserInfo.objects.get(id=xxx,None)
models.delete()
改:
obj = models.UserInfo.objects.get(id=xx,None)
obj = new_xxx
obj.save()
查:
querylist=models.Entry.objects.all()
print([e.title for e in querylist])
print([e.title for e in querylist])
entry = models.Entry.objects.get(id=?)
将复杂的数据结构,例如ORM中的QuerySet或者Model实例对象转换成Python内置的数据类型,从而进一步方便数据和JSON,XML等格式的数据进行交互。
1、DEMO:
models.py:
from django.db import models
# Create your models here.
class Graph(models.Model):
id = models.AutoField(primary_key=True)
graph_name = models.CharField(max_length=255)
graph_content = models.TextField()
graph_status = models.BooleanField(default=False)
graph_conf = models.CharField(max_length=255)
graph_position = models.IntegerField()
create_time = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'graph'
serilizers.py:
# -*- coding: utf-8 -*-
# @Time : 2019/5/8 11:07
# @Author : Zcs
# @File : serializers.py
from rest_framework import serializers
from .models import Graph
class GraphSerializer(serializers.ModelSerializer):
class Meta:
model = Graph
fields = '__all__'
可以在django shell中查看定义的序列化器的内容:
2、您可以将exclude
属性设置为要从序列化程序中排除的字段列表
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
exclude = ('users',)
常见问题:
1、Cannot call `.is_valid()` as no `data=` keyword argument was passed when instantiating the serializer instance.
当把query_set序列化成Python数据类型时,将data作为第一个参数传给serilizer即可,但是反序列化时,要指定data=data
https://stackoverflow.com/questions/29731013/django-rest-framework-cannot-call-is-valid-as-no-data-keyword-argument/29731923
使用传统的视图,需要为总体和实例个体分别实现视图函数:
from django.urls import path
from snippets import views
urlpatterns = [
path('snippets/', views.snippet_list),
path('snippets//', views.snippet_detail),
]
@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)
对于snippet_list来说,只有get方法和post方法。put方法和delete方法都是针对某个实例而言的,所以放在snippet_detail中。
可以添加序列化字段对时间进行格式化:
date_joined = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
命名规范:
URL: path('ports/', VIEWSET_CONF['port_list']),
path('ports//', VIEWSET_CONF['port_detail']),
VIEWSET: class PortViewSet(ModelViewSet):
SERIALIZER: class TargetSerializer(serializers.ModelSerializer):
MODEL: class Schedule(models.Model):
1、普通URL + APIView + ModelSerializer
urlpatterns = [
path('dev/', views.GraphView.as_view()),
]
"""
from rest_framework import routers
# router = routers.DefaultRouter()
# router.register('dev', views.DeViewset) # 为viewset注册路由
这种方式是将dev_list和dev_detail分开的注册方式,下面的方式2可以
将dev_list和dev_detail使用同一个viewset和serializer,通过请求方法的
不同调用不同的处理函数
"""
class GraphView(APIView):
"""
GET: [{'graph_name':'', 'graph_content':'', 'graph_status':'', 'upper_limit':'', 'lower_limit':'', 'size':'',
'graph_position':'', 'create_time'},
]
POST:{'graph_name':'', 'graph_status':'', 'upper_limit':'', 'lower_limit':'', 'size':''},
"""
def get(self, request):
query_data = Graph.objects.all() # 从数据库取出数据,query_set
serializer = GraphSerializer(query_data, many=True) # 将query_set序列化成Python数据类型
# data = JSONRenderer().render(serializer.data) # 将Python数据类型转成JSON
# print(data)
return Response(serializer.data)
def post(self, request):
"""
graph_conf 2|5|10000
反序列化,将JSON形式数据转换为流的形式,然后将流数据转化为Python数据类型
:param request:
:return:
"""
stream = BytesIO(request.body) # 将JSON数据转换为流形式
data = JSONParser().parse(stream) # 解析流中的JSON数据并转换为Python数据结构
serializer = GraphSerializer(data=data)
if serializer.is_valid(): # 验证前台传过来的数据是否合法,在save前必须调用该方法
serializer.save(**data) # 根据是否存在instance实例来决定执行create方法或update方法,无执行create
# save方法可以选择传参或者不传参,不传参的话默认会从serializer实例化时传入的数据进行读取
# 但是这样读取的话,非model定义的字段会被舍弃掉,所以如果需要传入非model定义的字段,需要在save方法传入data
return 0
# print(serializer.errors)
def patch(self, request):
stream = BytesIO(request.body)
data = JSONParser().parse(stream)
serializer = GraphSerializer(data=data)
serializer.instance = Graph.objects.get(id=data['id'])
if serializer.is_valid():
serializer.save(**data)
return 0
def delete(self, request):
"""
RESTful默认界面的DELETE方法会将URL中的id传递给后台,如果没有使用URL传参的话,是不会传过来数据的
自己传递JSON过来使用以下方法解析即可
:param request:
:return:
"""
stream = BytesIO(request.body)
data = JSONParser().parse(stream)
g_id = data['id']
obj = Graph.objects.get(id=g_id)
obj.delete()
return Response({"status": 0})
class GraphSerializer(serializers.ModelSerializer):
class Meta:
model = Graph
fields = '__all__'
read_only_fields = ['graph_name', 'graph_content', 'graph_conf', 'graph_position', 'create_time']
# read_only_fields选项,表示该字段只用作查询出结果,不需要用户传值
# 如果未设置该字段,那么表示所有字段都需要从serializer那传值,如果未传is_valid函数会返回False
# extra_kwargs = {'id': {'write_only': True}}
# 提供extra_kwargs的方式来为某个字段指定其关键字参数,而不需要去显示指定(id=serializers.IntegerField())
@staticmethod
def create_graph(gspace, gtime, num):
buff = ()
for i in range(num):
ret1 = random.randrange(gspace[0], gspace[1] + 1)
ret2 = random.randrange(gtime[0], gtime[1] + 1)
buff += ((ret1, ret2),)
return buff
# 验证用户传到后台的数据是否合法
# def validate(self, attrs):
# return attrs
def create(self, validated_data):
print(validated_data)
validated_data['graph_conf'] = str(validated_data['lower_limit']) + '|' + \
str(validated_data['upper_limit']) + '|' + str(validated_data['size'])
validated_data['graph_position'] = 0
validated_data['graph_content'] = str(self.create_graph([1,4], [validated_data['lower_limit'],validated_data['upper_limit']],10))
kwargs = {
'graph_name': validated_data.get('graph_name'),
'graph_content': validated_data.get('graph_content'),
'graph_status': validated_data.get('graph_status'),
'graph_conf': validated_data.get('graph_conf'),
'graph_position': validated_data.get('graph_position'),
}
instance = Graph.objects.create(**kwargs)
return instance
def update(self, instance, validated_data):
# 只允许更新graph_status参数
instance.graph_status = validated_data['graph_status']
instance.save()
return instance
2.正则URL + viewsets.ModelViewSet + ModelSerializer
from django.urls import path, include
from . import views
from rest_framework.urlpatterns import format_suffix_patterns
dev_list = views.DeViewset.as_view({
'get': 'list',
'post': 'create'
})
dev_detail = views.DeViewset.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
})
urlpatterns = format_suffix_patterns([
#path('', include(router.urls)),
path('graph/', views.GraphView.as_view()),
path('devs/', dev_list),
path('devs//', dev_detail)
])
class DeViewset(viewsets.ModelViewSet):
"""
ModelViewset继承了四个混类和一个泛类,自动会实现增删查改的方法
viewset相当于是视图的集合,综合了多个方法
"""
queryset = Dev.objects.all().order_by('id')
serializer_class = DevSerializer
lookup_field = 'id' # 定义通过哪个参数来定位实例
class DevSerializer(serializers.ModelSerializer):
class Meta:
model = Dev
fields = '__all__'
models.py:定义一个私有方法
class Dev(models.Model):
id = models.AutoField(primary_key=True)
cpu = models.IntegerField()
memory = models.IntegerField()
disk = models.IntegerField()
ip = models.CharField(max_length=15)
dev_user = models.ForeignKey(User, on_delete=models.CASCADE, default='')
class Meta:
db_table = 'dev'
@property
def dev_user_name(self):
return self.dev_user.username
serilizers.py:需要注意的是,dev_user_name变量名必须与model的方法名保持一致
class DevSerializer(serializers.ModelSerializer):
dev_user_name = serializers.ReadOnlyField()
class Meta:
model = Dev
fields = '__all__'
http://www.cnblogs.com/pgxpython/articles/10144398.html
在serializer层面也可以做到同样的事情:
class DevSerializer(serializers.ModelSerializer):
dev_user_name = serializers.ReadOnlyField()
is_superuser = serializers.SerializerMethodField()
class Meta:
model = Dev
fields = '__all__'
@staticmethod
def get_is_superuser(obj):
return obj.dev_user.is_superuser
1、向视图添加所需权限:
from rest_framework import permissions
class DeViewset(viewsets.ModelViewSet):
"""
ModelViewset继承了四个混类和一个泛类,自动会实现增删查改的方法
viewset相当于是视图的集合,综合了多个方法
"""
queryset = Dev.objects.all().order_by('id')
serializer_class = DevSerializer
lookup_field = 'id' # 定义通过哪个参数来定位实例
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
# 没有认证的情况下只有只读权限
可以通过逻辑符号组合多个permission类,前提是这些类都继承于BasePermission
# -*- coding: utf-8 -*-
# @Time : 2019/6/13 10:44
# @Author : Zcs
# @File : permissions.py
from rest_framework.permissions import BasePermission
# class UserReadAdminWrite(BasePermission):
# """
# 普通用户拥有只读权限,admin用户拥有读写权限
# """
# def has_permission(self, request, view):
# if request.user and request.user.is_staff:
# return True
# elif request.user and request.user.is_authenticated and request.method in ['GET']:
# return True
# 三权分立,系统管理员,安全审计员,安全管理员
class SysAdminPermission(BasePermission):
def has_permission(self, request, view):
if request.user.is_authenticated and request.user.user_type == 1:
return True
class AudAdminPermission(BasePermission):
def has_permission(self, request, view):
if request.user.is_authenticated and request.user.user_type == 2:
return True
class SecAdminPermission(BasePermission):
def has_permission(self, request, view):
if request.user.is_authenticated and request.user.user_type == 3:
return True
class SupAdminPermission(BasePermission):
def has_permission(self, request, view):
if request.user.is_authenticated and request.user.user_type == 4:
return True
permission_classes = (SecAdminPermission | AudAdminPermission,)
valid_kwargs = {
'read_only', 'write_only',
'required', 'default', 'initial', 'source',
'label', 'help_text', 'style',
'error_messages', 'validators', 'allow_null', 'allow_blank',
'choices'
}
read_only_fields = ['is_use', 'uniq_id', 'max_']
1.设置了read_only_fields,那么用户无法从该接口修改该字段,提交了该字段不会报错,但是不会修改该字段的值。