0X-1 踩坑記錄
如何統一處理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/<uuid:pk>/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 # 分頁
關於serializer驗證數據的機制,在進行serializer.save()之前,必須運行serializer.is_valid()方法,這個方法首先會執行默認在models或者serializer裏面定義的字段約束,如果不滿足該約束直接返回異常。如果通過了字段約束,會進入validate()方法,該方法默認直接返回原始值,用戶可以對該方法重寫,加入自己想要進行的數據驗證。整個驗證結束後,過濾後的值會被賦值給serializer.data
序列化器會將前臺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.
serializers.Serializer並不支持在class meta中定義model,fields等,需要顯式定義序列化字段。而ModelSerializer是支持的
關於depth參數(針對ModelSerializer):
使用該參數可以輕鬆獲得外鍵或多對多關係的具體數據,而不是單單顯示一個key,缺點是使用了該參數後無法使用create和update方法,存在外鍵關係的字段會被過濾掉。
解決方法:重寫create和update方法,將使用的serializer指向另一個serializer,一個除了沒有指定depth參數,其餘皆和原serializer相同的序列化器。
0X-2 反向查詢
方法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被引用的次數。
0X-3 碰到的小知識點
獲取實例的類名:
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:只能上傳鍵值對,並且鍵值對都是間隔分開的。
0X-4 使用filter來進行查詢、排序等操作
相關文檔:
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
0X-5 在APIView中使用分頁器
在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
0X-6 返回一個文件
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
0X-7 上傳一個文件
支持通過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)
0X00 什麼是ORM
- MVC框架中包括一個重要的部分,就是ORM,它實現了數據模型與數據庫的解耦,即數據模型的設計不需要依賴於特定的數據庫,通過簡單的配置就可以輕鬆更換數據庫
- ORM是“對象-關係-映射”的簡稱,主要任務是:
- 根據對象的類型生成表結構
- 將對象、列表的操作,轉換爲sql語句
- 將sql查詢到的結果轉換爲對象、列表
- 這極大的減輕了開發人員的工作量,不需要面對因數據庫變更而導致的無效勞動
- Django中的模型包含存儲數據的字段和約束,對應着數據庫中唯一的表
0X01 ORM中的屬性及關係
- 定義屬性時,需要字段類型
- 字段類型被定義在django.db.models.fields目錄下,爲了方便使用,被導入到django.db.models中
- 使用方式
- 導入from django.db import models
- 通過models.Field創建字段類型的對象,賦值給屬性
- 對於重要數據都做邏輯刪除,不做物理刪除,實現方法是定義isDelete屬性,類型爲BooleanField,默認值爲False
字段類型
獲取choice字段的描述:
如果設置了choice類型,那麼非設置的值不允許傳入,傳入會報錯:
{
"credit_type": [
"“3” 不是合法選項。"
]
}
choice = (
(1, 'up'), # username + password
(2, 'snmp') # snmp community
)
credit_type = models.IntegerField(choices=choice)
# 前臺傳參需要傳遞int類型
- AutoField:一個根據實際ID自動增長的IntegerField,通常不指定
- 如果不指定,一個主鍵字段將自動添加到模型中
- BooleanField:true/false 字段,此字段的默認表單控制是CheckboxInput
- NullBooleanField:支持null、true、false三種值
- CharField(max_length=字符長度):字符串,默認的表單樣式是 TextInput
- TextField:大文本字段,一般超過4000使用,默認的表單控件是Textarea
- IntegerField:整數
- DecimalField(max_digits=None, decimal_places=None):使用python的Decimal實例表示的十進制浮點數
- DecimalField.max_digits:位數總數
- DecimalField.decimal_places:小數點後的數字位數
- FloatField:用Python的float實例來表示的浮點數
- DateField[auto_now=False, auto_now_add=False]):使用Python的datetime.date實例表示的日期
- 參數DateField.auto_now:每次保存對象時,自動設置該字段爲當前時間,用於"最後一次修改"的時間戳,它總是使用當前日期,默認爲false
- 參數DateField.auto_now_add:當對象第一次被創建時自動設置當前時間,用於創建的時間戳,它總是使用當前日期,默認爲false
- 該字段默認對應的表單控件是一個TextInput. 在管理員站點添加了一個JavaScript寫的日曆控件,和一個“Today"的快捷按鈕,包含了一個額外的invalid_date錯誤消息鍵
- auto_now_add, auto_now, and default 這些設置是相互排斥的,他們之間的任何組合將會發生錯誤的結果
- TimeField:使用Python的datetime.time實例表示的時間,參數同DateField
- DateTimeField:使用Python的datetime.datetime實例表示的日期和時間,參數同DateField
- FileField:一個上傳文件的字段
- ImageField:繼承了FileField的所有屬性和方法,但對上傳的對象進行校驗,確保它是個有效的image
字段選項
- 通過字段選項,可以實現對字段的約束
- 在字段對象時通過關鍵字參數指定
- null:如果爲True,Django 將空值以NULL 存儲到數據庫中,默認值是 False
- blank:如果爲True,則該字段允許爲空白,默認值是 False
- 對比:null是數據庫範疇的概念,blank是表單驗證證範疇的
- db_column:字段的名稱,如果未指定,則使用屬性的名稱
- db_index:若值爲 True, 則在表中會爲此字段創建索引
- default:默認值
- primary_key:若爲 True, 則該字段會成爲模型的主鍵字段
- unique:如果爲 True, 這個字段在表中必須有唯一值
關係
- 關係的類型包括
- ForeignKey:一對多,將字段定義在多的端中,如果一個model中有多個外鍵指向同一個model,需要設置related_name,避免反向訪問時衝突(可以通過related_name來進行反向查詢!)
- ManyToManyField:多對多,將字段定義在兩端中
- OneToOneField:一對一,將字段定義在任意一端中
- 可以維護遞歸的關聯關係,使用'self'指定,詳見“自關聯”
- 用一訪問多:對象.模型類小寫_set
使用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'
0X02 常見的操作
增:
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=?)
0X03 rest_framework的serializer
將複雜的數據結構,例如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',)
好文章:https://www.cnblogs.com/pyspark/p/8607801.html
常見問題:
1、Cannot call `.is_valid()` as no `data=` keyword argument was passed when instantiating the serializer instance.
當把query_set序列化成Python數據類型時,將data作爲第一個參數傳給serilizer即可,但是反序列化時,要指定data=data
使用傳統的視圖,需要爲總體和實例個體分別實現視圖函數:
from django.urls import path
from snippets import views
urlpatterns = [
path('snippets/', views.snippet_list),
path('snippets/<int:pk>/', 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)
0X04 幾種url;view;serializer的組合
命名規範:
URL: path('ports/', VIEWSET_CONF['port_list']),
path('ports/<str:pk>/', 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/<int:id>/', 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__'
drf中的view繼承關係:
0X05 當存在外鍵關係時,默認取出的數據只包含外鍵id,如何根據外鍵id取出其他所有數據?
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
0X06 rest_framework中的權限管理
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,)
0X07 serializer中的幾個fields屬性
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,那麼用戶無法從該接口修改該字段,提交了該字段不會報錯,但是不會修改該字段的值。