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
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。