OJ实现记录(职工列表、职工详情)之后端django学习篇

OJ实现记录(职工列表、职工详情)之后端django学习篇

我们后端采用的环境是django,因为小组的每一个都不是很了解这个框架,所以在实现之前,我们先进行了学习。针对本次小组接收到的任务,来大致总结一下自己在编写api前学到的知识。

django中的模块(Model)与数据库

首先我们将我们模块用到的数据表分析一下

User
Group
Role

第一张图讲的是为用户划分权限,每个用户会有自己的分组,每个分组对应一个角色,从而通过找到user就可以知道他的角色是什么了。

Gender
User
UserStatus
Teacher
Admin

第二张图就展示了user表的关系。User里面包含了Gender对象和UserStatus对象,Teacher和Admin又包含了User对象。

数据库相应部分了解完毕,我们便使用django建立对应的类。django中的model可以在集成之后会自动生成与模块相关的用户表。我们先以下列代码为例:

class Gender(models.Model):
    """性别"""

    name = models.CharField(max_length=20, unique=True)

    @staticmethod
    def default():
        return 1


class UserStatus(models.Model):
    """用户状态"""

    name = models.CharField(max_length=20, unique=True)

    @staticmethod
    def default():
        return 1

Gender表继承了django的model类,虽然里面只有一个属性,但实际生成的数据表中还会有一个自动产生的自增的主键id,在这个表中设置默认值为1,为了之后的使用。UserStatus表同理。

而User表中我们继承了django中auth权限中的user基类(AbstractUser),因为在的django中,auth中的user和groups是关联的,这是django实现对用户权限进行管理的功能。我们借助这个工具,可以直接在创建用户的时候为其划分分组,从而在未来权限的分配功能,更加健全。

class User(AbstractUser):
    """自定义用户基类"""

    name = models.CharField(max_length=20)
    user_status = models.ForeignKey(UserStatus, default=UserStatus.default, on_delete=models.SET_DEFAULT)
    email = models.EmailField(null=True, blank=True)
    gender = models.ForeignKey(Gender, default=Gender.default, on_delete=models.SET_DEFAULT)

    USERNAME_FIELD = 'username'

在User类中,引用UserStatus类和Gender类作外键,外键的默认值就是引用类的默认值,在删除数据(on.delete)时,设置该属性的默认值为引用类的默认值(models.SET_DEFAULT),也就是当我们删除UserStatus的某条数据,与之关联的User表中的数据改为设置的default值。

在一些属性设置字段的参数null为True,意味该属性的值可以为空,而默认值是false,意味该值不能为空。

值得注意的是,当我们在把自定义的User类当做的auth的用户基类使用时,需要找到去修改它的设置,来到项目容器文件夹oj下的配置文件settings.py,加入以下一行:

AUTH_USER_MODEL = 'user.User'

user是app文件夹,User是我们定义的基类

之后是我们关联了User类的其他两个类:Admin和Teacher

class Teacher(models.Model):
    """教师"""

    teacher_number = models.CharField(max_length=20, unique=True)
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)


class Admin(models.Model):
    """管理员"""

    admin_number = models.CharField(max_length=20, unique=True)
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

在这两个类中,我们发现admin_number属性的字段参数unique为True,说明这个属性值是惟一的,在创建数据的时候不能重复。

并且,这两个类与User类一对一定义,在我的理解,这与外键的使用与意义是一样的,唯一不同的地方就是函数的参数不同,settings.AUTH_USER_MODEL就是我们在之前修改的配置文件中的属性,CASCADE意思是删除关联数据时,与之关联的数据也删除,当我们删除User中的数据时,与之关联的Admin和Teacher中对应的那一条数据都要删除。

最后就是我们用到的角色类role,它继承了auth中的group,所以需要引用auth模块(还引用了别的模块但是我没写)。

from django.contrib.auth.models import AbstractUser, Group


class Role(models.Model):
    description = models.CharField(max_length=200)
    group = models.OneToOneField(Group, on_delete=models.CASCADE, primary_key=True, default="")
    permission = models.ManyToManyField(Permission)

这里也用到了OneToOneField,并且把group设置为了主键,而Permission类由于不需要我也没有去研究,也没有研究ManyToManyField。

在实际的应用中,我们在集成之后,关联数据在数据库以其主键(id)存放在被关联的数据中,比如userstatus_id, gender_id

第一个后端api和django rest framework

在实现前后端连接的时候,我便按照我们负责的模块的第一个功能去写一个api——用户列表的api,这个api简单在于我们并不需要接受的数据,只要调用api,我们就可以直接返回我们想要传输的数据。

因为前后端传输的数据是以json格式传递的,所以我们必须将数据库的数据转换为json数据。我们之所以引入了django rest framework(简称drf),也是因为这个框架很好地帮助我们转换数据。

先上代码:

from utils.api import APIView, JSONResponse
from ..serializers import TeacherSerializers, AdminSerializers
from ..models import  Admin, Teacher


class GetStaffListAPI(APIView):
    """
    获取staff列表信息
    """
    def get(self, request):
        admin = Admin.objects.all()
        admin_serializers = AdminSerializers(admin, many=True)
        teachers = Teacher.objects.all()
        teachers_serializers = TeacherSerializers(teachers, many=True)
        datalist = admin_serializers.data + teachers_serializers.data
        return self.success(datalist)

GetStaffListAPI其实就是我们所说的api接口,它继承了drf中的APIView接口,这个接口里就包含了g等待实现的get和post方法(还有http协议的各种方法,因为没有用到也就没有学习)。

首先我们先看这个方法的返回值,这个方法主要的任务是将我们的数据传递出去,self.success()函数就是能传递函数的方法,它其实在utils目录下的api.py文件下定义了:

from rest_framework.response import Response
from rest_framework.views import APIView as RestfulAPIView


class APIView(RestfulAPIView):
    """
    Django view的父类, 和django-rest-framework的用法基本一致
     - request.data获取解析之后的json或者urlencoded数据, dict类型
     - self.success, self.error和self.invalid_serializer可以根据业需求修改,
        写到父类中是为了不同的人开发写法统一,不再使用自己的success/error格式
     - self.response 返回一个django HttpResponse, 具体在self.response_class中实现
     - parse请求的类需要定义在request_parser中, 目前只支持json和urlencoded的类型, 用来解析请求的数据
    """

    @staticmethod
    def success(data=None):
        return Response({"error": None, "data": data})

    @staticmethod
    def error(msg="error", err="error"):
        return Response({"error": err, "data": msg})

其实我们只是实际上只是调用的drf中的Response函数,将其封装到APIView的success和error方法,而Response的参数直接就是json类型。success方法的参数data即可以是对象,也可以是列表。

然后我们再看这个get方法接下来写了什么。

		admin = Admin.objects.all()
        admin_serializers = AdminSerializers(admin, many=True)

我们通过类名.objects.all()可以得到该类的所有对象,实际上就是这个数据表中的每一条记录。因为我们其实得到的是一个对象的列表,而不是json数据,所以我们将其序列化,drf的官方文档就教给我们一个十分简单的序列化方法,这个方法不仅强在写法简便,而且可以对多个特殊类型的字段进行序列化,比如image等。

我们在serializers.py文件下创建serializers

from rest_framework import serializers
from .models import Gender, UserStatus, User, Admin, Teacher
from django.db.utils import IntegrityError


class GenderSerializers(serializers.ModelSerializer):

    class Meta:
        model = Gender
        fields = '__all__'


class UserStatusSerializers(serializers.ModelSerializer):

    class Meta:
        model = UserStatus
        fields = '__all__'


class UserSerializers(serializers.ModelSerializer):
    gender = GenderSerializers()
    user_status = UserStatusSerializers()

    class Meta:
        model = User
        fields = '__all__'


class AdminSerializers(serializers.ModelSerializer):
    user = UserSerializers()

    class Meta:
        model = Admin
        fields = '__all__'


class TeacherSerializers(serializers.ModelSerializer):
    user = UserSerializers()

    class Meta:
        model = Teacher
        fields = '__all__'

因为我们api需要职工的数据,包含了老师和管理员,所以我们需要将模块Admin和Teacher都模块化,因为他们都继承了User类,并且在数据中是以主键存储的,所以我们可以在序列化Admin和Teacher时,使用user = UserSerializers(),这可以作用于任何继承了其他类地模块的序列化过程。所以我们要先将User序列化,同理,在User序列化前要将模块Gender和UserStatus序列化。

序列化的之后的格式类似于这样(采用其中一条记录):

{
	id: 6,
	user: {
		id: 1,
		gender: {
			id: 2,
			name: "男"
		},
		user_status: {
			id: 1,
			name: "未知"
		},
		password: "pbkdf2_sha256$150000$2ik87knOxywx$Wj3RKAG2p0ezVoGGZSnBsJbuZlBBwgmq8nCRrAV/O5E=",
		last_login: "2019-05-26T00:57:08.792352+08:00",
		is_superuser: false,
		username: "Demo1",
		first_name: "Demo",
		last_name: "Demo",
		is_staff: false,
		is_active: true,
		date_joined: "2019-05-25T23:52:36.393000+08:00",
		name: "Student1",
		email: "",
		groups: [ ],
		user_permissions: [ ]
	},
	admin_number: "11111"
},

使用编写的serializer的语句admin_serializers = AdminSerializers(admin, many=True)
而在调用我们写的serializer时,需要传参需要序列化的对象(或对象列表),若是多个对象必须设定many=True,这会得到一个列表序列化后的数据,如果不设置这个值的话,就只能序列化一个对象,若传入参数为对象就会报错。

最后因为我们既要获得管理员的信息,又要获取老师的信息,所以我的做法是找到Admin所有对象并序列化,找到Teacher所有对象并序列化,最后去他们的并集。需要注意的是,因为我们得到的序列化对象(例如admin_serializers)并不是我们真正要传的数据,而需要取admin_serializers中data的值,所以直接datalist = admin_serializers.data + teachers_serializers.data,datalist就是最终我们传递的数据。


当然,drf还有很多有效便捷的操作,例如,可以自主实现数据的分页等。然而,由于本次的数据较小,再者本次的前端组件有输入数据会自动分页的功能,就没有用这个方法。但是,最后传递的数据是按照管理员和教师自动排序,并且因为层次太多,导致前端的逻辑较为复杂,在之后会将api重构优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值