【问题标题】:Model History in DjangoDjango 中的模型历史
【发布时间】:2012-12-25 22:55:44
【问题描述】:

在 Django 中,如何处理与 Post 对象关联的图像的并发更改?

这是一种以前曾提出过的问题,但并未完全涵盖相同的问题。我已经阅读了这些(questionquestionquestionquestion),但问题略有不同。

我有一个博客文章模型(速度的伪代码),其中包含标题、摘要和正文,以及相关的图像。

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 对象关联的图像的并发更改?这些将通过 Post 模型的 body 字段中的引用进行更改(这是一个 Markdown 格式的文本字段,然后在表单的 POST 上进行编辑,以添加到图像字段的 URL 引用中)。解决此问题的最佳方法是在修订版和 Post 对象上使用 M2M 字段,从而允许始终将图像与 PostRevision 对象一起存储?

【问题讨论】:

  • 我读得不好或误解了,但您是要保存每个字段修订版还是整个对象?
  • 好吧,这是困境的一部分,我是否每次都保存整个对象的差异(可能性 1)(对于许多字段,这基本上是“零更改”,或者保存更改的各个字段的差异(可能性 2)?
  • 如果一个修订与另一个修订具有相同的标题,那会很糟糕吗?
  • 不,这是完全可以接受的情况。假设我选择可能性 1,那么这就消除了匹配增量、字段和对象的问题,并使其保持相当原子性——一个修订就是一个修订。下一个问题(绑定图像字段),我可能会在图像和修订对象之间使用 M2M 来解决这个问题,这听起来合理吗?
  • 有一个应用程序可以做到这一点:django-simple-history.readthedocs.io/en/2.7.2/quick_start.html

标签: python django django-models version revision-history


【解决方案1】:

我认为你应该坚持选项 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()

【讨论】:

    【解决方案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 中的任何模型更改都会反映在 PostPostHistory 中。

    Image 被键入到 Post,所以事情保持整洁。您可以选择在 Post.save() 函数中清理图像,但我可能会选择 post_save 信号以保持代码更清晰。

    【讨论】:

    • 感谢以上 - 关于复制数据而不是差异,当文本可能有几千个字长时,你还会这么说吗?有些更改可能小到将句号更改为逗号?
    • 我不会预先优化,直到您有更多数据或存储空间是一个问题(也许压缩数据)。这取决于您将版本与另一个版本进行比较的频率。如果您的差异跟踪从版本到下一个版本的更改,那么您有相当多的数据要读取/处理,只是为了比较两个版本。如果你只是存储原始的差异,那么随着差异变大,你会失去更多的存储空间。无论哪种方式都是权衡取舍。
    • super(Post, self).save() 不会返回 post 实例。
    猜你喜欢
    • 2012-08-22
    • 2010-11-02
    • 1970-01-01
    • 2023-04-01
    • 2012-05-19
    • 1970-01-01
    • 2020-10-10
    • 2011-05-28
    • 2014-02-22
    相关资源
    最近更新 更多