django rest framework api构建

本文详细介绍Django REST框架的配置与应用,包括设置、模型、序列化器、视图集和路由器的使用,以及如何构建Web API。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

更多详细内容参考项目,以及官方文档Viewsetbase 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的具体内容。
这并不意味着这总是好的选择,就像在权衡使用基于类的视图还是是基于函数的视图一样。
运行起来的内容为:
api
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值