Django 5 для начинающих

Прогресс по курсу:  9/1004

5.2 Создание моделей данных блога
4 из 9 шагов пройдено
0 из 21 баллa  получено

Давайте посмотрим, как взаимодействовать с вариантами статуса. Выполните следующую ниже команду в командной оболочке, чтобы открыть оболочку Python:

python manage.py shell

Затем наберите такие строки:

from blog.models import Post
Post.Status.choices

Вы получите варианты перечисления в формате пар значение–метка, подобные показанным ниже:

[('DF', 'Draft'), ('PB', 'Published')]

Наберите следующую ниже строку:

Post.Status.labels

Вы получите удобочитаемые имена членов перечисления enum, как показано ниже:

['Draft', 'Published']

Наберите следующую ниже строку:

Post.Status.values

Вы получите значения элементов перечисления enum, как показано ниже. Эти значения можно сохранить в базе данных в поле status:

['DF', 'PB']

Наберите такую строку:

Post.Status.names

Вы получите имена вариантов, как показано ниже:

['DRAFT', 'PUBLISHED']

К конкретному искомому перечисляемому элементу можно обращаться посредством Post.Status.PUBLISHED, а также обращаться к его свойствам .name и .value. Чтобы выйти из режима shell используйте комбинацию клавиш Ctrl+Z.

 

Добавление взаимосвязи многие-к-одному

Посты всегда пишутся автором. Про организацию связей мы с вами говорили в разделе 3.4 "Организация связей между таблицами".

В данном разделе будет создана взаимосвязь между пользователями и постами, которая будет указывать на конкретных пользователей и написанные ими посты.

Django идет в комплекте с фреймворком аутентификации, который ведет учетные записи пользователей. Встроенный в Django фреймворк аутентификации располагается в пакете django.contrib.auth и содержит модель User(Пользователь).

Модель User будет применяться из указанного фреймворка аутентификации, чтобы создавать взаимосвязи между пользователями и постами.

Отредактируйте файл models.py приложения blog, придав ему следующий вид:

from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User


class Post(models.Model):
    class Status(models.TextChoices):
        DRAFT = 'DF', 'Draft'
        PUBLISHED = 'PB', 'Published'

    title = models.CharField(max_length=250)
    slug = models.SlugField(max_length=250, unique=True)
    author = models.ForeignKey(User,
                               on_delete=models.CASCADE,
                               related_name='blog_posts')

    body = models.TextField()

    publish = models.DateTimeField(default=timezone.now)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    status = models.CharField(max_length=2,
                              choices=Status.choices,
                              default=Status.DRAFT)

    class Meta:
        ordering = ['-publish']
        indexes = [
            models.Index(fields=['-publish']),
        ]

    def __str__(self):
        return self.title

Мы импортировали модель User из модуля django.contrib.auth.models и добавили в модель Post поле author.

Это поле определяет взаимосвязь многие-к-одному, означающую, что каждый пост написан пользователем и пользователь может написать любое число постов. Для этого поля Django создаст внешний ключ в базе данных, используя первичный ключ соответствующей модели.

Параметр on_delete определяет поведение, которое следует применять при удалении объекта, на который есть ссылка. Это поведение не относится конкретно к Django; оно является стандартным для SQL.

Использование ключевого слова CASCADE указывает на то, что при удалении пользователя, на которого есть ссылка, база данных также удалит все связанные с ним посты в блоге.

Со всеми возможными опциями можно ознакомиться по адресу https://docs.djangoproject.com/en/5.0/ref/models/fields/#django.db.models.ForeignKey.on_delete

Мы используем related_name, чтобы указывать имя обратной связи, от User к Post. Такой подход позволит легко обращаться к связанным объектам из объекта User, используя обозначение user.blog_posts. Подробнее об этом мы узнаем позже.

Django содержит разные типы полей, которые можно использовать для определения своих моделей. Все типы полей находятся на странице https://docs.djangoproject.com/en/5.0/ref/models/fields/.

Теперь модель Post завершена, и сейчас можно синхронизировать ее с базой данных.


  • Комментариев
Будьте вежливы и соблюдайте наши принципы сообщества. Пожалуйста, не оставляйте решения и подсказки в комментариях, для этого есть отдельный форум.
Оставить комментарий

Имя приложения для related_name можно указать более гибко и динамично:

author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='%(app_label)s_posts')
https://docs.djangoproject.com/en/5.0/topics/db/models/#abstract-related-name
Изменен Сергей Королев

Здравствуйте! Прежде чем перейти на шаг:

python manage.py shell

 Делаю миграцию:

python manage.py makemigrations

А затем:

python manage.py migrate

И вот тут происходит ошибка:

django.core.exceptions.ValidationError: ['“UTC” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format.']

Настройки settings.py:

TIME_ZONE = 'UTC'

USE_I18N = True

USE_TZ = True

models.py:

from django.db import models
from django.utils import timezone


class Post(models.Model):
    class Status(models.TextChoices):
        DRAFT = 'DF', 'Draft'
        PUBLISHED = 'PB', 'Published'

    title = models.CharField(max_length=250)
    slug = models.SlugField(max_length=250, unique=True)
    body = models.TextField()

    publish = models.DateTimeField(default=timezone.now)
    created = models.DateTimeField(auto_now_add=True)
    update = models.DateTimeField(auto_now=True)
    status = models.CharField(max_length=2, choices=Status.choices, default=Status.DRAFT)

    class Meta:
        ordering = ['-publish']
        indexes = [
            models.Index(fields=['-publish']),
        ]

    def __str__(self):
        return self.title

Подскажите пожалуйста, что не так?

@Мария_Толстова, на первый взгляд ошибок не видно, загрузите архив проекта по ссылке https://mega.nz/filerequest/rANtUqzWHQ4 посмотрим в чём причина.

@Мария_Толстова, было ли запущено виртуальное окружение и какая версия Django(python -m django --version)?

@Дмитрий_Селезнев, версия 4.0.6

@Дмитрий_Селезнев, подгрузила

@Мария_Толстова, там ошибка в файле миграций 0002_alter_post_options_post_created_post_publish_and_more.py, откройте его, и найдите эту строку:

field=models.DateTimeField(auto_now_add=True, default='UTC'),

и замените её на эту:

field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),

после сохранения файла можно применить миграцию.

Но лучше обновить Django до 4.2 или до 5.0:

Для этого удаляете файл БД db.sqlite3, и файлы миграций 0001_initial.py и 0002_alter_post_options_post_created_post_publish_and_more.py, удаляете старую Django:

pip uninstall django

и устанавливаете новую

pip install django

Не забывайте, виртуальное окружение должно быть при этом активировано.

После этого необходимо создать и применить миграции:

python manage.py makemigrations
python manage.py migrate

@Дмитрий_Селезнев, ооо, спасибо большое, заработало!!!)))))

Про организацию связей мы с вами говорили в разделе 2.8 "Организация связей между таблицами".

Уже раздел 3.4 и ссылка выдает 403

@Evg_Ne, спасибо, исправил.

К конкретному искомому перечисляемому элементу можно обращаться посредством Post.Status.PUBLISHED, а также обращаться к его свойствам .name и .value.

И к свойству .label

А категории/подкатегории постов? 

@Олег_Пешков, В этом курсе, для блога, мы не будем реализовывать данный функционал. Можете добавить по такому принципу:

class Category(models.Model):
       name = models.CharField(max_length=255)

class SubCategory(models.Model):
       parent_category = models.ForeignKey(Category, on_delete=models.DO_NOTHING) # связь с категорией
       name = models.CharField(max_length=255)
    
class Post(models.Model):
    class Status(models.TextChoices):
        DRAFT = 'DF', 'Draft'
        PUBLISHED = 'PB', 'Published'

    title = models.CharField(max_length=250)
    sub_category = models.ForeignKey(SubCategory, on_delete=models.DO_NOTHING) # связь с суб-категорией
    slug = models.SlugField(max_length=250)
    author = models.ForeignKey(User,
                               on_delete=models.CASCADE,
                               related_name='blog_posts')

    body = models.TextField()
....
...
..
.

@Илья_Перминов, Спасибо! А есть возможность чтобы все это отображалось по принципу древовидной структуры?) И с названиями еще желательно?

@Олег_Пешков, Да и вообще желательно чтобы это все из одной вкладки в админке выполнялось (а то если так плодить вкладки, то будет как на каком-нибудь wordpress с кучей плагинов))). Как пример реализации данной функции для курса тоже хороший был бы. Или это уже на следующем курсе разбирается?)

 

Две минуты в гугле и нашел как это реализуется, правда через mptt model.

Изменен Олег Пешков

@Олег_Пешков, В следующем курсе такого нет, там уже рассматриваем совсем другие технологии разработки. А так, Django имеет кучу плагинов, и с их помощью можно решить любую задачу.