Найти - Пользователи
Полная версия: Правильно переопределить save в моделе.
Начало » Django » Правильно переопределить save в моделе.
1
agryn
В моей моделе для новостей сделал дополнительное поле content_preview - неформатированый plain text для предварительного просмотра начала новости. Хочу переопределить метод save для того что б во время сохранения (вызова метода save) в это поле заносились необходимые данные. В качестве хранения текста новости использую markdown соотвецтвенно данные храняться в MarkupField. Когда я попробовал сделать так:
# -*- coding: utf-8 -*-
from django.db import models
from markitup.fields import MarkupField
from django.template.defaultfilters import striptags
class Source(models.Model):
    """
    Модель для опису джерел отримання новин
    """
    name = models.CharField("Джерело", max_length=200, help_text='назва джерела новин')
    name_full = models.CharField("Джерело (повна назва)", blank=True, max_length=600, help_text='повна назва джерела новин')
    site = models.URLField("Сайт", help_text='сайт джерела інформації')
    def __unicode__(self):
        return self.name
    class Meta:
        verbose_name = 'Джерело новини'
        verbose_name_plural = 'Джерела новин'
class Article(models.Model):
    """
    Модель для новин
    """
    title = models.CharField("Заголовок", max_length=200)
    content_preview = models.TextField("Попередній текст", editable=False, blank=True, help_text='Початокова частина статті (неформатований plain текст генеруэться з markdown)')
    content = MarkupField("Cтаття в markdown", help_text='Текст статті в розмітці markdown')
    orig_date = models.DateTimeField("Дата оригінальної публікації", default=None, null=True, blank=True, help_text='Дата публікації в оригіналі статті')
    pars_date = models.DateTimeField("Дата парсинга", editable=False, default=None, null=True, blank=True, help_text='Дата коли стаття була добавлеа парсером в БД')
    site_date = models.DateTimeField("Дата публікації", help_text='Дата публыкації що буде відображатись на нашому сайті')
    author = models.CharField("Автор статті", max_length=50, blank=True, help_text='Автор з оригінальної статті') ## автор статті з
    orig_url = models.URLField("URL посилання", blank=True)
    source = models.ForeignKey(Source)
    is_published = models.BooleanField("Публікація")
    def save(self, *args, **kwargs):
        self.content_preview = striptags(self.content._get_rendered())
        super(Article, self).save(*args, **kwargs)
        
    def __unicode__(self):
        return self.title
    class Meta:
        verbose_name = 'Новина'
        verbose_name_plural = 'Новини'
        ordering = ['-site_date']
то данные в поле content_preview обновляються только при повторном вызове save.
Подскажите как правильно сгенерировать данные в поле content_preview
ilnur
не совсем понял.
опишите ситуацию. Делаю так мол так, а вот это не получается.
agryn
ilnur
не совсем понял.опишите ситуацию. Делаю так мол так, а вот это не получается.
При создании новой записи через админку, с использованием описанной выше модели, поле content_preview не обновляется. А я хочу чтоб в это поле автоматом заносились данные при нажатии кнопки “Сохранить” в админке.
ilnur
попробуй что-то своё туда записать.
def save(self, *args, **kwargs):
        self.content_preview = u'тестовый текст'
        super(Article, self).save(*args, **kwargs)
может self.content._get_rendered() возвращает пустоту
agryn
ilnur
попробуй что-то своё туда записать.
Да self.content._get_rendered() возвращает пустоту и вообще все атрибуты модели в етом месте кода пустые, в том то и проблема что мне нужно вызвать s
elf.content_preview = striptags(self.content._get_rendered())
в тот момент когда атрибуты уже со значениями а комит(запись в БД) еще не произведен.
Как вариант вызвать после save (тогда уже точно все атрибуты екземпляра класа присвоены). Но тогда нужно повторный запрос к БД для записи/обновления (для меня повторный запрос не критичен). Я попробовал сделать это с помощью сигналов:
@receiver(post_save, sender = Article)
def generate_preview(instance, **kwargs):
    article = instance
    article.content_preview = striptags(article.content.rendered)
    article.save()
Но данная конструкция вызывает себя рекурсивно. А как сделать без рекурсии пока не дойду.
ilnur
у меня все нормально, в save передастся экземпляр модели с заполненными полями

class Category(models.Model):
    name = models.CharField(
        verbose_name='Название', 
        max_length=100)
    url = models.CharField(
        verbose_name='url',
        max_length=100,
        blank=True)
    def save(self, *args, **kwargs):
        self.url = self.name
        super(Category, self).save(*args, **kwargs)
FishHook
ilnur,
не горячись, попробуй то же самое сделать ближе к задаче ТСа, то есть тебе нужно реализовать
content_preview = models.TextField("", editable=False, blank=True, help_text='')
content = MarkupField("", help_text='')
def save(self, *args, **kwargs):
        self.content_preview = striptags(self.content._get_rendered())
        super(Article, self).save(*args, **kwargs)
чтобы убедиться в том, что ТС гонит
А ТС, судя по всему не совсем нуб и задает не пустой вопрос.

мне лень

agryn,
а что должен возвращать метод _get_rendered()?
Если не внутри сейва, а просто
foo = Article.objects.get(pk=1).content._get_rendered()
то что получается?
agryn
Article.objects.get(pk=1).content._get_rendered()
Возвращает отрендереный с markdowd html-код.
Вся соль вопроса в том что покопался в исходниках markitup я так и непонял на кокой стадии (а самое главное где в коде) производиться генерирование с markdowd-кода html-код.
agryn
Короче вопрос решил вот таким возможно не самым элегантным с точки зрения использования ресурсов (двойной запрос при сохранении записи в БД, а также generate_preview вызываеться рекурсивно один раз) но рабочим кодом:
from django.template.defaultfilters import striptags, truncatewords
from django.db.models.signals import post_save
from django.dispatch import receiver
...
@receiver(post_save, sender = Article)
def generate_preview(instance, **kwargs):
    content_preview = striptags(instance.content.rendered)
    content_preview = truncatewords(content_preview, 35)
    if instance.content_preview != content_preview:
        instance.content_preview = content_preview
        instance.save()
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB