Языки программирования

Связи между моделями

В нашем проекте есть одна модель под названием «Объявления». В этой статье мы создадим новый класс модели «Rubric», которая будет представлять рубрики объявлений. Допишем в модуль models.py пакета приложения «btest»:
class Rubric(models.Model):
  name = models.CharField(max_length=20, db_index=True,
  verbose_name='Название')
  
  class Meta:
    verbose_name_plural = 'Рубрики'
    verbose_name = 'Рубрика'
    ordering = ['name']
Новая модель содержит одно объявленное поле «name», которое будет хранить название рубрики. Для этого поля мы сразу велели создать индекс, т.к. будем выводить перечень рубрик отсортированным по их названиям.

Осталось добавить в модель Bb поле внешнего ключа, устанавливающее связь между текущей записью этой модели и записью модели Rubric, т.е. между объявлением и рубрикой, к которой оно относится.

Таким образом будет создана связь «один-со-многими», при которой одна запись модели Rubric будет связана с произвольным количеством записей модели Bb.

Создадим в последней такое поле, назвав его rubric. Изменим код класса Bb:
class Bb(models.Model):
  title = models.CharField(max_length=50, verbose_name='Товар')
  content = models.TextField(null=True, blank=True, verbose_name='Описание')
  price = models.FloatField(null=True, blank=True, verbose_name='Цена')
  published = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Опубликовано')
  rubric = models.ForeignKey('Rubric', null=True,
  on_delete=models.PROTECT, verbose_name='Рубрика')
  
  class Meta:
    verbose_name_plural = 'Объявления'
    verbose_name = 'Объявление'
    ordering = ['-published']
Класс ForeignKey представляет поле внешнего ключа, в котором фактически будет храниться ключ записи из первичной модели. Первым параметром конструктору этого класса передается класс первичной модели в виде:

  • ссылки на класс — если код, объявляющий класс первичной модели, располагается перед кодом класса вторичной модели;
  • строки с именем класса — если вторичная модель объявлена раньше первичной.

Все поля, создаваемые в моделях, по умолчанию обязательны к заполнению. Следовательно, добавить новое, обязательное к заполнению поле в модель, которая уже содержит записи, нельзя — сама СУБД откажется делать это и выведет сообщение об ошибке.

Необходимо пометить поле rubric как необязательное, присвоив параметру null значение True — только после этого будет успешно добавлено в модуль.

Именованный параметр on_delete управляет каскадными удалениями записей вторичной модели после удаления записи первичной модели, с которой они были связаны. Значение PROTECT этого параметра запрещает каскадные удаления.

Сохраним исправленный модуль и выполним генерирование миграций, которые внесут необходимые изменения в структуры базы данных:
manage.py makemigrations btest
После выполнения данной команды в папке migrations будет создан модуль миграции с именем вида «002_auto_отметка текущей даты и времени.py». Выполним созданную миграцию:
manage.py migrate
Зарегистрируем новую модель на административном сайте, добавив в модуль admin.py пакета приложения такие два выражения:
from .models import Rubric
...
admin.site.register(Rubric)
Сохраним файл, запустим отладочный web-сервер и зайдем на административный сайт:
Добавим новую рубрику:

Строковое представление модели

Наша рубрика создалась, но она представляется строкой вида «имя класса модели object значение ключа записи». Необходимо переопределить в классе модели метод __str__(), возвращающий строковое представление класса.
Откроем модуль models.py и изменим в объявление класса модели Rubric код:
class Rubric(models.Model):
  name = models.CharField(max_length=20, db_index=True,
  verbose_name='Название')
  def __str__(self):
    return self.name
  
  class Meta:
    verbose_name_plural = 'Рубрики'
    verbose_name = 'Рубрика'
    ordering = ['name']
Сохраните файл, обновим страницу списка рубрик и посмотрим, что у нас получилось:
Создадим необходимые рубрики и добавим во все наши объявления соответствующую рубрику:
Осталось сделать так, чтобы в списке записей модели Bb, выводились рубрики объявлений. Для этого необходимо в последовательность имен полей, присвоенную атрибуту list_display класса BbAdmin, поле rubric (в файле admin.py):
list_display = ('title', 'content', 'price', 'published', 'rubric')
Сохраним файл и обновим страницу списка объявлений — и увидим новый столбец:
Фреймворк Django