文章目录
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的具体内容。
这并不意味着这总是好的选择,就像在权衡使用基于类的视图还是是基于函数的视图一样。
运行起来的内容为: