banner
raye~

Raye's Journey

且趁闲身未老,尽放我、些子疏狂。
medium
tg_channel
twitter
github
email
nintendo switch
playstation
steam_profiles

【Django后台开发二】模型设计与创建

【Django 后台开发一】环境部署和初始化项目

需求抽象与模型设计#

后台开发本质不就是 CRUD 吗 😂 /doge ,因此先从数据模型上进行设计

上文有提到过需求,主要功能包括发布学习内容,学生管理,制定学习计划,学生打卡

这里有三种角色,

  • 管理员:可以有所有的功能
  • 学习委员:每个班级一个学习委员,负责制定本班的学习推送计划
  • 学生:可以接收学习内容,并打卡

因此我们可以思考下,抽象出表

  • 学习内容表,记录学习内容
  • 班级表,需要有个学委的角色
  • 学生表,三种角色
  • 打卡表,每个学生的打卡记录
  • 推送策略,同时关联学习内容和班级

学习内容表,包含学习内容、标题、简介、学习链接,为了避免直接删除,加了一个是否上线的判断来标记删除

class StudyContent(models.Model):
    """
    学习内容,包含标题,简介,学习链接
    """
    title = models.CharField(max_length=100, verbose_name='标题')
    brief = models.TextField(verbose_name='简介')
    url = models.URLField(verbose_name='页面链接')
    is_online = models.BooleanField(default=False, verbose_name='学习内容是否上线')
    class Meta:
        verbose_name = '学习内容'
        verbose_name_plural = '学习内容'

    def __str__(self):
        return self.title

verbose_name 是为了能在后台展示的更清晰,不然就是一个 Object

学生表、班级表,学生表是需要引用班级表的外键,并且班级表也需要引用学生表作为外键

这里就遇到了第一个坑,根据上文描述这里其实存在循环依赖,导致后续新建用户失败

因此加上 black=True 来允许创建一个没有学委的班级,也允许创建一个学生,但是不指定班级

class SchoolClass(models.Model):
    """
    班级表,每个学生属于班级,每个班级一个学委
    """
    name = models.CharField(max_length=100, verbose_name='班级名')
    study_committee = models.OneToOneField(
        'Student', 
        on_delete=models.SET_NULL, 
        null=True, 
        blank=True,  # 允许先创建一个没有学委的班级
        related_name='managed_class',
        verbose_name='学习委员'
    )
    class Meta:
        verbose_name = '班级管理'
        verbose_name_plural = '班级管理'

    def __str__(self):
        return self.name


class Student(AbstractUser):
    """
    角色表
    """
    STUDENT = 'ST'
    ADMIN = 'AD'
    STUDY_COMMITTEE = 'SC'
    ROLE_CHOICES = [
        (STUDENT, '学生'),
        (ADMIN, '管理员'),
        (STUDY_COMMITTEE, '学习委员')
    ]
    role = models.CharField(
        max_length=2, choices=ROLE_CHOICES, default=STUDENT, verbose_name='角色类型')
    class_group = models.ForeignKey(SchoolClass, on_delete=models.SET_NULL, null=True, blank=True, related_name='students', verbose_name='所属班级')
    is_staff = models.BooleanField(default=True) # 默认允许登录后台

    def __str__(self):
        return self.username

    class Meta:
        verbose_name = '用户管理'
        verbose_name_plural = '用户管理'

为了复用 Django 后台的用户管理功能,我们让 Student 类继承自 AbstractUser
后续在 admin 中还需要添加如下代码,这是后话

@admin.register(Student)
class StudentAdmin(UserAdmin):
  pass

推送策略表,打卡记录表

打卡表关联了学习内容做外键
推送策略表同事关联了学习内容、班级做外键

class Checkin(models.Model):
    """
    学生打卡表
    """
    student = models.ForeignKey(Student, on_delete=models.CASCADE, verbose_name='学生')
    study_content = models.ForeignKey(
        StudyContent, on_delete=models.CASCADE, related_name='checkins', verbose_name='学习内容')
    checkin_time = models.DateTimeField(auto_now_add=True, verbose_name='打卡时间')
    is_valid = models.BooleanField(default=True, verbose_name='打卡记录是否有效') # 默认标记为有效
    class Meta:
        verbose_name = '学生打卡记录管理'
        verbose_name_plural = '学生打卡记录管理'

    def __str__(self):
        return f"{self.student}{self.checkin_time} 打卡了 {self.study_content}"

class PushStrategy(models.Model):
    """
    推送策略,外键关联内容,班级、推送频率和推送时间
    """
    study_content = models.ForeignKey(
        StudyContent, on_delete=models.CASCADE, related_name='push_strategy', verbose_name='学习内容')
    class_group = models.ForeignKey('SchoolClass', on_delete=models.CASCADE, related_name='push_strategies', verbose_name='班级')
    frequency = models.CharField(max_length=20, verbose_name='推送频率')
    push_time = models.DateTimeField(verbose_name='推送开始时间')
    is_online = models.BooleanField(default=False, verbose_name='推送策略是否上线')

    class Meta:
        verbose_name = '推送策略管理'
        verbose_name_plural = '推送策略管理'
        unique_together = [['study_content', 'class_group']] # 保证这两个键唯一

    def __str__(self):
        return self.study_content.title

至此我们的模型就基本开发完了,每次修改完模型,都需要运行

python manage.py makemigrations
python manage.py migrate
加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。