文章目錄
Introductions
what is django rest framework
Django REST框架是一個功能強大且靈活的工具包,用於構建Web API。
An example for basedata
1. settings
INSTALLED_APPS = [
'suit',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# local
'basedata',
'news',
'reports',
'analysis',
'common',
#Thrid Part
'rest_framework',
'rest_framework_jwt',
'ckeditor',
'ckeditor_uploader',
'rest_framework.authtoken',
'rest_auth'
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
# 'oauth2_provider.ext.rest_framework.OAuth2Authentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'PAGE_SIZE': 10,
'DEFAULT_PAGINATION_CLASS': 'common.paginations.DefaultPagination',
'URL_FIELD_NAME': 'api_url',
'DEFAULT_FILTER_BACKENDS': (
'django_filters.rest_framework.DjangoFilterBackend', 'rest_framework.filters.SearchFilter'
),
'TEST_REQUEST_DEFAULT_FORMAT': 'json'
}
以上爲django 在 rest framework 的配置項
2. model
此次以user的擴展屬性介紹api實現流程,代碼如下:
import os
import uuid
from PIL import Image
from ckeditor.fields import RichTextField
from django.contrib.auth.models import User
from django.db import models
# Create your models here.
from django.utils.six import text_type
from tdc import settings
class UserProfile(models.Model):
NATURE = (
('國企', '國企'),
('私企', '私企'),
('外資', '外資'),
('其他', '其他'),)
user = models.OneToOneField(User, verbose_name='用戶', on_delete=models.CASCADE, )
company_name = models.CharField(verbose_name='企業名稱', blank=True, null=True, max_length=settings.DB_CHAR_NAME_40)
company_code = models.CharField(verbose_name='企業代碼', blank=True, null=True, max_length=settings.DB_CHAR_NAME_40)
credit_code = models.CharField(verbose_name='社會信用代碼', blank=True, null=True, max_length=settings.DB_CHAR_NAME_40)
company_nature = models.CharField(verbose_name='企業性質', choices=NATURE, blank=True,
max_length=settings.DB_CHAR_NAME_20)
registration_type = models.CharField(verbose_name='登記註冊類型', blank=True, null=True,
max_length=settings.DB_CHAR_NAME_40)
carbon_trade = models.BooleanField(verbose_name='是否碳交易企業', blank=True, default=True)
industry = models.ForeignKey(Industry, verbose_name='所屬行業', on_delete=models.CASCADE, related_name='user_industry')
link_person = models.CharField(verbose_name='聯繫人姓名', blank=True, null=True, max_length=settings.DB_CHAR_NAME_20)
moble = models.CharField(verbose_name='聯繫人電話', max_length=settings.DB_CHAR_NAME_20, blank=True, null=True)
email = models.EmailField(verbose_name='郵箱', blank=True, null=True, max_length=settings.DB_CHAR_NAME_20)
business_address = models.CharField(verbose_name='經營地址', blank=True, default='',
max_length=settings.DB_CHAR_NAME_100)
settingsruction_area = models.FloatField(verbose_name='建築面積', blank=True, null=True,
max_length=settings.DB_CHAR_NAME_40)
product = RichTextField(null=True)
class Meta:
verbose_name = '企業信息'
verbose_name_plural = verbose_name
def __str__(self):
return self.user.username if (self.company_name is None) else self.company_name
@property
def industry_name(self):
return self.industry.industry_type
@property
def group_name(self):
try:
return self.user.groups.all()[0].name
except:
return ''
@property
def carbon_trade_name(self):
if self.carbon_trade:
return '%s' % ('是')
else:
return '%s' % ('否')
def scramble_uploaded_filename(instance, filename):
"""
Scramble / uglify the filename of the uploaded file, but keep the files extension (e.g., .jpg or .png)
:param instance:
:param filename:
:return:
"""
extension = filename.split(".")[-1]
return "{}.{}".format(uuid.uuid4(), extension)
def create_thumbnail(input_image, thumbnail_size=(500, 300)):
"""
Create a thumbnail of an existing image
:param input_image:
:param thumbnail_size:
:return:
"""
if not input_image or input_image == "":
return
try:
image = Image.open(input_image)
except IOError:
print (' in IOError')
return
mode = image.mode
if mode not in ('L', 'RGB'):
if mode == 'RGBA':
# 透明圖片需要加白色底
image.load()
alpha = image.split()[3]
bgmask = alpha.point(lambda x: 255 - x)
image = image.convert('RGB')
# paste(color, box, mask)
image.paste((255, 255, 255), None, bgmask)
else:
image = image.convert('RGB')
# make sure an image has been set
#
# # open image
# image = Image.open(input_image)
# use PILs thumbnail method; use anti aliasing to make the scaled picture look good
image.thumbnail(thumbnail_size, Image.ANTIALIAS)
# parse the filename and scramble it
filename = scramble_uploaded_filename(None, os.path.basename(input_image.name))
arrdata = filename.split(".")
# extension is in the last element, pop it
extension = arrdata.pop()
basename = "".join(arrdata)
# add _thumb to the filename
new_filename = basename + "_thumb." + extension
# save the image in MEDIA_ROOT and return the filename
image.save(os.path.join(settings.MEDIA_ROOT, new_filename))
return new_filename
class ProductImage(models.Model):
user = models.ForeignKey(User, verbose_name='用戶', on_delete=models.CASCADE)
url = models.ImageField(upload_to='images/%Y/%m/%d', null=False, blank=False, verbose_name='圖片url')
thum_img = models.ImageField(null=True, blank=True, verbose_name='壓縮圖片')
class Meta:
verbose_name = '圖片'
verbose_name_plural = verbose_name
def save(self, *args, **kwargs):
self.thum_img=create_thumbnail(self.url)
super(ProductImage, self).save(*args, **kwargs)
上圖配置,進行了基本信息,圖片,副文本等模型創建。
3. serializers
因爲前後端分離放棄了view,templeta,jijia,所以精簡,只需將數據進行序列化,傳入前臺,方便處理數據。
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = (
'id', 'company_name', 'company_code', 'credit_code', 'company_nature', 'registration_type', 'carbon_trade',
'industry_name', 'link_person', 'moble', 'email', 'business_address', 'settingsruction_area',
'product','group_name','carbon_trade_name')
read_only_fields = ('id',)
class UpdateUserProfileSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = UserProfile
fields = '__all__'
def create(self, validated_data):
return UserProfile.objects.create(**validated_data)
class ProductImageSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = ProductImage
fields = ('user', 'url','id','thum_img')
serializer 跟django的form是非常相像。在字段中包含required , max_length和default 等驗證標誌,此處採用的是繼承model的類型,省略大部分代碼。故,不需要重複填寫驗證。同時,也可以在此處自定義驗證方式。字段標誌還可以控制在某些情況下應該如何顯示序列化程序,例如在呈現HTML時。 上面的{‘base_template’: ‘textarea.html’}標誌等同於在Django Form類上使用widget=widgets.Textarea。
對於數據展示類型,我們傾向於json,便於前後端交互。外鍵處理,以及更多內容需查看官方文檔serializer
4. use viewset prefer view
REST框架包含一個ViewSets 的抽象行爲,它允許開發人員能專注於API的狀態和相互作用進行建模,並基於常規約定自動構建URL。
ViewSet 類和 View 類幾乎是一樣的,區別是它提供了類似於read 或 update 這樣的操作, 而不處理像 get 或者 put 這樣的方法。
一個ViewSet 類當被實例化成一組視圖時, 通常會通過使用一個路由類(Router class)來幫你處理複雜的URL定義,最終綁定到一組處理方法。
處理viewset基本都是重構已有的類,也可自己重寫類。該示例的代碼如下:
class UserViewset(mixins.ListModelMixin, mixins.UpdateModelMixin, GenericViewSet):
"""
用戶的基本信息的查看,編輯
"""
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication) # 訪問該視圖需要驗證身份信息,將使用這些類
def get_queryset(self):
return UserProfile.objects.filter(user=self.request.user)
def get_serializer_class(self):
if self.action == 'list':
return UserProfileSerializer
return UpdateUserProfileSerializer
class UplodaViewSet(viewsets.ModelViewSet):
"""創建圖片
"""
permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
queryset = ProductImage.objects.all()
serializer_class = ProductImageSerializer
因爲基本信息是在採用django signal 機制自動生成,故api中不需要創建以及刪除的方法,沒有繼承modelViewSet,只有list和update,以及基本GenericViewSet,permission_classes是檢查用戶對model的權限,authentication_classes是網頁驗證的方法,都可以進行二次開發。其中IsOwnerOrReadOnly的代碼如下:
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Object-level permission to only allow owners of an object to edit it.
Assumes the model instance has an `owner` attribute.
"""
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 obj.user == request.user:
return True
# Instance must have an attribute name "owner"
return obj.user == request.user
更多詳細內容參考項目,以及官方文檔Viewset,base view
5. router
由於我們使用 ViewSet 類而不是View 類, 我們實際上不需要自己設計URL配置。 通過一個 Router 類會自動處理 視圖和 url 連接資源。 我們所需要做的就是通過路由註冊一個適當的視圖集。
現在我們重寫urls.py 文件:
rounter = DefaultRouter()
rounter.register(r'users', UserViewset, base_name='users')
rounter.register(r'reports', ReportViewSet, base_name='reports')
rounter.register(r'fossil', CCategoryOneFViewSet, base_name='fossil')
rounter.register(r'electricity', CElectricityCViewSet, base_name='electricity')
rounter.register(r'heat', CHeatCViewSet, base_name='heat')
rounter.register(r'news', NewViewSet, base_name='news')
rounter.register(r'pdf',PDFViewSet,base_name='pdf')
rounter.register(r'upload', UplodaViewSet, base_name='upload')
使用 viewsets 可以是一個非常有用的抽象。 它有助於確保您的API中的URL約定是一致的,最大限度地減少您編寫的代碼量,並允許您專注於API提供的交互和表示,而不是URL conf的具體內容。
這並不意味着這總是好的選擇,就像在權衡使用基於類的視圖還是是基於函數的視圖一樣。
運行起來的內容爲: