Перейти к содержанию

Модели

Нам необходимо описать БД средствами Django ORM, для этого перейдём в файл models.py в наших приложениях и создадим несколько классов, наследуемых от django.db.models.Model.

Также внутри каждого класса создадим мета-класс с 3 полями:

  • db_table - название таблицы в БД;
  • verbose_name - человекочитаемое название объекта;
  • verbose_name_plural - человекочитаемое название во множественном числе.

Кроме того, переопределим дандер-метод __str__ в каждом классе, чтобы объекты имели более понятное текстовое представление.

Адаптеры
from django.db import models


class Adapter(models.Model):
    class Meta:
        db_table = "adapter"
        verbose_name = "адаптер"
        verbose_name_plural = "адаптеры"

    first_name = models.CharField(max_length=50, verbose_name="Имя")
    last_name = models.CharField(max_length=50, verbose_name="Фамилия")

    def __str__(self):
        return f"{self.last_name} {self.first_name}"

В этой модели определим только два текстовых поля: фамилию и имя. Остальная информация этого приложения уже имеется на проде, и она не нужна для работы опроса.

Профили ITMO.ID
from django.contrib.auth.models import User
from django.db import models

class ItmoIdProfile(models.Model):
    class Meta:
        db_table = "itmo_id_profile"
        verbose_name = "профиль ITMO.ID"
        verbose_name_plural = "профили ITMO.ID"

    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile", verbose_name="Пользователь")
    isu = models.PositiveIntegerField(null=True, blank=True, verbose_name="ИСУ")
    course = models.PositiveSmallIntegerField(null=True, blank=True, verbose_name="Курс")
    faculty = models.CharField(blank=True, max_length=50, verbose_name="Факультет")
    group = models.CharField(blank=True, max_length=7, verbose_name="Группа")

    def __str__(self):
        return f"{self.user.last_name} {self.user.first_name} {'(' + str(self.isu) + ')' if self.isu else ''}"

В этой модели определим O2O связь к встроенной модели auth.User, чтобы расширить её дополнительной необязательной информацией. Здесь будет храниться необходимая информация, полученная с ITMO.ID.

from django.db import models


class SurveyGroup(models.Model):
    class Meta:
        db_table = "survey_group"
        verbose_name = "группа"
        verbose_name_plural = "группы"

    name = models.CharField(max_length=7, verbose_name="Название")
    students_count = models.PositiveSmallIntegerField(null=True, blank=True, verbose_name="Количество студентов")
    adapters = models.ManyToManyField("adapter.Adapter", blank=True, related_name="groups", verbose_name="Адаптеры")

    def __str__(self):
        return self.name

В этой модели определим поле name, значения которого должны совпадать с тем, что возвращает ITMO.ID, для дальнейшего сопоставления. Также укажем поле students_count, необходимое для дальнейшего подсчёта баллов для каждого адаптера для оценивания на ПГАС. И привяжем с помощью M2M связи модель адаптеров так же для подсчёта баллов и чтобы в опросе выводить только необходимых адаптеров.

from django.db import models


class SurveyFaculty(models.Model):
    class Meta:
        db_table = "survey_faculty"
        verbose_name = "факультет"
        verbose_name_plural = "факультеты"

    name = models.CharField(max_length=50, verbose_name="Название")
    is_active = models.BooleanField(default=False, verbose_name="Активен?")

    def __str__(self):
        return self.name

В этой модели определим поле name, значения которого должны совпадать с тем, что возвращает ITMO.ID. И определим булеое поле is_active, на которое будем ориентироваться для постепенного открытия опроса.

from django.db import models


class SurveyQuestion(models.Model):
    class Meta:
        db_table = "survey_question"
        verbose_name = "вопрос"
        verbose_name_plural = "вопросы"

    class Type(models.TextChoices):
        TEXT_AREA = "text_area", "TextArea"
        ADAPTER_SLIDER = "adapter_slider", "AdapterSlider"
        SLIDER = "slider", "Slider"
        SELECT = "select", "Select"
        TRAINING_SELECT = "training_select", "TrainingSelect"

    component = models.CharField(max_length=15, choices=Type.choices, verbose_name="Виджет")
    text = models.CharField(max_length=200, verbose_name="Основной текст")
    help_text = models.CharField(max_length=200, blank=True, verbose_name="Дополнительный текст")
    training_text = models.CharField(
        max_length=200,
        blank=True,
        verbose_name="Второй основной текст",
        help_text="Используется для слайдера в TrainingSelect виджете",
    )
    training_help_text = models.CharField(
        max_length=200,
        blank=True,
        verbose_name="Второй дополнительный текст",
        help_text="Используется для слайдера в TrainingSelect виджете",
    )
    order = models.PositiveSmallIntegerField(verbose_name="Порядок")

    def __str__(self):
        return f"{self.component} - {self.text}"

В этой модели определим текстовое поле 'component', допустимыми значениями которого будут названия кастомных Vue.js компонентов, вынесенные в Djang Enum - TextChoices. Благодаря этому полю фронтенд сможет понять, какой компонент отрисовывать для каждого вопроса. Даже добавим поля text и help_text для отображения надписи над компонентом крупным и мелким шрифтом, и дополнительные поля training_text и training_help_text в случае использования компонента AdapterSelect, который внутри себя создаёт два компонента, каждый из который нуждается в надписи. Также определим поле order, необходимое для сортировки в необходимом порядке.

from django.db import models


class SurveyAnswer(models.Model):
    class Meta:
        db_table = "survey_answer"
        verbose_name = "ответ"
        verbose_name_plural = "ответы"

    user = models.ForeignKey("auth.User", on_delete=models.CASCADE, verbose_name="Пользователь", related_name="answers")
    adapter = models.ForeignKey(
        "adapter.Adapter", blank=True, null=True, on_delete=models.CASCADE, verbose_name="Адаптер"
    )
    question = models.ForeignKey(
        SurveyQuestion, on_delete=models.CASCADE, verbose_name="Вопрос", related_name="answers"
    )
    value = models.CharField(max_length=1000, verbose_name="Ответ")
    score = models.FloatField(blank=True, null=True, verbose_name="Балл")

    def __str__(self):
        return f"{self.value} {self.question.text}"

В этой модели укажем три внешних ключа: к встроенной модели auth.User, чтобы понимать, какой пользователь отправил ответ, к модели Adapter, чтобы в случае компонента AdapterSlider понимать, какому именно адаптеру была поставлена оценка, и к модели SurveyQuestion, чтобы понимать, на какой именно вопрос пришёл ответ. Также укажем два поля для хранения значений ответов, текстовое value, хранящее прямой ответ с "формы", и численное score, хранящее адаптированное значение баллов.

После создания всех моделей нужно обязательно сделать миграции:

python manage.py makemigrations
python manage.py migrate

А так же создать суперпользователя:

python manage.py createsuperuser