История моделей в Django


Это своеобразный вопрос, который был задан ранее, но не совсем охватывающий те же самые вопросы. Я прочитал эти (Вопрос, Вопрос, Вопрос , и вопрос), но вопрос немного отличается.

У меня есть модель записи в блоге (псевдокод для скорости), которая содержит заголовок, аннотацию и тело, а также связанные с ними изображения.
class Post(models.Model):
    title = CharField
    abstract = TextField
    body = TextField

class Image(models.Model):
    post = ForeignKey(Post)
    imagefile = ImageField

Теперь я хочу добавить возможность хранить истории изменений в этом Post модель. Я подумал о двух возможностях для этого:

Возможность 1

class PostHistory(models.Model):
    post = ForeignKey(Post)
    title_delta = TextField
    abstract_delta = TextField
    body_delta = TextField

Однако это имеет проблему, что он хранит дельты без изменений (например, когда title не изменяется, и есть только Дельта для поля body. Тем не менее, когда изменяется более одного поля, это соответствует тому, что "1 ревизия == 1 полная ревизия".

Возможность 2

class PostRevision(models.Model):
    post = ForeignKey(Post)
    field = CharField #Field name
    delta = TextField

Благодаря двум различным подходам это успешно дает мне историю различий для поля, которое я бы сгенерировал с помощьюdiff-match-patch (немного более производительный, чем встроенныйdifflib ). Два вопроса, которые я сейчас имею, связаны с генерацией основных объектов (т. е. с верхней ревизией в цепочке).

Задается вопрос, как мне тогда справиться с одновременными изменениями изображений, связанных с объектом Post, поскольку они будут изменены через ссылки в поле body Модели Post (это форматированный текст Markdown поле, которое затем редактируется на POST формы, чтобы добавить в URL ссылки для поля изображения. Является ли лучшим способом справиться с этим, чтобы использовать поле M2M на ревизии и на объекте Post, позволяя всегда сохранять изображения с объектом PostRevision?

2 2

2 ответа:

Я согласен с @rickard-zachrisson, что вы должны придерживаться подхода №1. Я бы сделал несколько тонких изменений, хотя (псевдокод кстати):

class AbstractPost(models.Model):
    title = CharField
    abstract = TextField
    body = TextField

    class Meta:
        abstract = True


class Post(AbstractPost):
    def save(self):
        post = super(Post, self).save()

        PostHistory.objects.create(
            post=post,
            title=post.title,
            abstract=post.abstract,
            body=post.body,
        )


class PostHistory(AbstractPost):
    post = ForeignKey(Post)

    class Meta:
        ordering = ['-pk']


class Image(models.Model):
    post = ForeignKey(Post)
    imagefile = ImageField

Ваша последняя версия всегда будет находиться в Post, а история изменений-в pk порядке PostHistory, который легко отличить от изменений. Я бы дублировал данные, потому что хранение дешево, а хранение дельт-это Пита. Если у вас есть несколько правок или вы хотите сравнить текущую версию с оригинальной версией, то дельты в основном бесполезны. Какой-нибудь изменения модели в AbstractPost отражены как в Post, так и в PostHistory.

Image он настроен на пост, чтобы вещи оставались аккуратными. Вы можете дополнительно очистить изображения в своем посте.функция save (), но я бы, вероятно, выбрал сигнал post_save, чтобы сохранить код чище.

Я думаю, вам следует придерживаться варианта 1.

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

class A(models.Model):
    field1 = ...
    field2 = ...

    def save():
        if bla_bla_updated:
            A_revisions.objects.create(
                         field1=self.fields1, field2=self.fields2,
                         a=self)
        super(A, self).save()

class A_revision(models.Model):
    field1 = ...
    field2 = ...
    a = models.ForeignKey(A)
    revision = models.IntegerField()

    def save():
        self.revision = (A_revision.objects.get(a=self.a)
                                    .order_by('id').revision) + 1
        super(A_revision, self).save()