Я работаю над своим первым проектом с Django, это личный блог.
Модель истории имеет поле «начало», то есть первые 100 символов самой истории
Я хочу создать начало с pre_save, но всегда получаю ошибку: в разделе администратора, когда я добавляю историю и оставляю начало пустым, Джанго говорит: «Это поле обязательно для заполнения»!!
Это очень хорошо работает в другом файле
Вот код:
from django.db import models
from django.db.models.signals import pre_save
from my_blog_tags.models import Tag
# Create your models here.
class Story(models.Model):
title = models.CharField(max_length=75)
story = models.TextField()
beginning = models.CharField(max_length=100)
tags = models.ManyToManyField(Tag, blank=True)
active = models.BooleanField(default=True)
views = models.IntegerField(default=0)
def __str__(self):
return self.title
class Meta:
verbose_name = "story"
verbose_name_plural = "stories"
def story_pre_save(sender, instance: Story, *args, **kwargs):
if not instance.beginning:
story = str(instance.story)
instance.beginning = story[:100]
pre_save.connect(story_pre_save, sender=Story)
Скрин из админки:
@WillemVanOnsem Имеет значение как собственное поле, поскольку его можно вручную установить на что-то другое. Я согласен с тем, что вы не должны использовать сигналы, если вам не нужно подключаться к поведению сторонних приложений.
@schwobaseggl: да, я согласен, что мы можем использовать это, если пользователь может сам определить начало, но если это всегда «нарезанная» версия .story, то это не должно храниться в базе данных. Но вы правы, что пользователь может определить другое начало.
Опять же, метод get_beginning а-ля return self.beginning or self.story[:100] был бы проще, чем рисковать в этой глуши :-), особенно если вы когда-нибудь захотите изменить начальную логику по умолчанию.
Вы имеете в виду, что лучше добавить метод в ModelManger для возврата первых 100 символов поля истории?
@Мохаммадджавад: да. Сигналы не очень надежны: некоторые вызовы ORM, такие как .bulk_create(..) и .update(..), обходят сигналы, а это означает, что есть способы создавать/обновлять истории без соответствующего обновления .beginning. Даже если это сработает, становится неясно, что именно происходит, когда вы сохраняете объект модели. Только в редких случаях сигналы полезны.
@WillemVanOnsem, если это не очень хорошо, как я могу сделать это по-другому: модель тега имеет поля title и in_url, in_url' такая же, как заголовок, но с replace(' ','-') в функции pre_save
@Mohammadjavad: похоже, вы делаете свой собственный slugify с частью replace (docs.djangoproject.com/en/3.1/ref/utils/… ). Вместо этого я бы посоветовал использовать slugify. Вам лучше переопределить метод def save() и слагифицировать там, или вы установите его в представлении. Вы также можете использовать AutoSlugField из django-autoslug, чтобы просто указать, что должно быть слагифицировано, и тогда вам не нужно заботиться об этом самостоятельно.
Ошибка, которую вы получаете, связана с тем, что beginning является обязательным полем. Вы можете разрешить элементу формы быть пустым с помощью пустого = True в соответствующем поле модели:
class Story(models.Model):
# …
beginning = models.CharField(max_length=100, blank=True)
Я бы также посоветовал не использовать сигналы для установки поля beginning, если оно оставлено пустым. Сигналы — это антипаттерн. Они полезны, если у вас есть модели в другом приложении, но если у вас есть контроль над моделью, работать с сигналами не рекомендуется.
Более элегантный способ решить эту проблему может быть предложен @schwobaseggl, где у вас есть свойство, которое либо берет начало, либо нарезает историю, поэтому:
class Story(models.Model):
def get_beginning(self):
return self.beginning or self.story[:100]
Вы можете использовать это в шаблоне с {{ mystory.get_beginning }}. Если вы не укажете .beginning самостоятельно, а позже обновите .story, то get_beginning автоматически вернет обновленную нарезанную версию.
Он хочет сохранить первые 100 символов поля истории в начальном поле. переопределение метода сохранения должно быть хорошим
@RezaGH: проблема в том, что если вы позже измените story, то он больше не будет «обновляться». Это также сделало бы базу данных больше, так как теперь она в основном копирует первые 100 символов в beginning, это также замедляет запрос, потому что для возврата данных из базы данных требуется больше «пропускной способности». Так что это дублирующийся data-antipattern
Так что, само начальное поле нам больше не нужно?
@RezaGH: нет. У вас есть поле beginning, где вы можете ввести другое начало, если это необходимо. Дело в том, что у нас есть оба поля, но мы выполняем логику выбора либо self.beginning, либо self.story[:100] во время выполнения. Это означает, что если вы позже измените self.beginning на другое начало, self.get_beginning() вернет это начало, а не нарезанный вариант истории.
@RezaGH: это позволяет, как говорит schwobaseggl, автоматически использовать нарезанную историю или указать собственное начало.
Спасибо за помощь и объяснение <3
Нет необходимости использовать синглы, вы можете переопределить метод save. Чтобы сделать это для вас. вам просто нужно сделать что-то вроде этого
class Story(models.Model):
beginning = models.CharField(max_length=100, blank=True, null=True)
def save(self, *args, **kwargs):
self.beginning = self.story[0:100]
return super().save(*args, **kwargs)
Он автоматически сохранит первые 100 символов вашей истории.
Пожалуйста, покажите полную трассировку. Я бы настоятельно не советовал использовать сигналы. Сигналы — это антипаттерн: lincolnloop.com/blog/django-anti-patterns-signals специально для этого. Вам здесь не нужно поле beginning, во-первых, я думаю, это просто свойство.