由于多次涉及到了这个东东,又不是很理解机制,决定深入研究下。

what

  django-revision到底啥玩意?readthedocs上只有一句话概括:django-reversion can be used to add a powerful rollback and recovery facility to your admin site

而且只是说明了能干嘛:给admin后台管理提供强大的回滚和恢复功能。

  下面我们来看看,啥叫作强大的回滚和恢复。

首先,新建一个项目: test_revision,过程略。

新增模型: articles,包含以下字段:

 
序号 栏位 说明
1 title 标题
2 content 内容
3 author 作者
4 status 状态
5 create_date 创建日期
6 publish_date 发布日期

并注册到管理后台。因为只需要看后台,所以views/template/forms全部略。只需要写models 和 admin.py 如下:

models.py

from django.db import models

# Create your models here.
class article(models.Model):
title = models.CharField(max_length=128)
content = models.TextField()
author = models.CharField(max_length=32)
status_list=(('draft','draft'),('pending','pending'),('published','published'))
status = models.CharField(max_length=16, choices=status_list)
create_date = models.DateTimeField(auto_now_add=True)
publish_date = models.DateTimeField()

admin.py

from django.contrib import admin
import reversion
from .models import article
# Register your models here.
class articleAdmin(reversion.VersionAdmin):
list_display = ("id", "title", "author", "status","create_date") admin.site.register(article,articleAdmin)

project文件夹结构:

settings.py的installed_app中添加了“reversion",它涉及到的表是reversion_revision/ reversion_version,如下:

再把test_app加入到installed_app中并syncdb一下,过程略,直接runserver,查看效果,如下:

ok,测试下,

1). 回滚操作:

2). 数据恢复操作:

点击"recover deleted articles"(右侧),选择某个时间对应的对象进入详细,要恢复点击保存即可,如下:

awesome and impressive!

How?

原理:

权威解释看这里:http://django-reversion.readthedocs.org/en/latest/how-it-works.html

这里简单总结下:

被注册,标记,子类化的模型,在其被create/update时会在version表里面做相应的记录,记录的内容就是模型的数据序列化后的json表达式。当需要恢复时,将json表达式反序列化出来,填充到表单里面去,让用户自己选择回滚到哪。至于恢复,则是恢复到最后一次操作后的状态。

如何使用:

以下方式都是在Admin.py中进行。

  1. 继承方式,需要继承reversion.VersionAdmin,适用于新开发的app,如下:

import reversion

class YourModelAdmin(reversion.VersionAdmin):

    pass

admin.site.register(YourModel, YourModelAdmin)

  2. patch_admin注册方式,适用于已存在的尚未使用reversion的app,如下:

from reversion.helpers import patch_admin

patch_admin(User)

  对于尚未在admin.py中注册或者继承versionAdmin的模型,如果要实现版本控制有两个选择:

1). 在admin.py中注册或者继承VersionAdmin

2). 使用reversion的api进行控制,

使用reversion.register()进行注册/修饰对应的模型类.

具体参考这里

后记

发现一个有意思的问题,对于一个已经使用了reversion支持的模型:如果非admin后台修改了记录(update/delete),则delete的数据可以还原,update则无记录。实现如下:

views.py

from django.shortcuts import render
from django.views.generic.edit import UpdateView,DeleteView
from .models import article
from django.shortcuts import render_to_response
# Create your views here. class articleDV(DeleteView):
model = article
success_url = "/admin/test_app/article/"
template_name = "article_delete.html" class articleUV(UpdateView):
model = article
success_url = "/admin/test_app/article/"
template_name = "article_update.html"

解决方案:

适用于django-reversion 1.8.4

采用中间件(可以不用在admin里面继承VersionAdmin),在installed_app中加入中间件"reversion.middleware.RevisionMiddleware",同时修改django-reversion的reversion.py文件(windows则在C:\Python27\Lib\site-packages\django_reversion-1.8.4-py2.7.egg\reversion\revisions.py)的171~182行(原码如下):

                       manager.save_revision(
dict(
(obj, callable(data) and data() or data)
for obj, data
in manager_context.items()
),
user = self._user,
comment = self._comment,
meta = self._meta,
ignore_duplicates = self._ignore_duplicates,
db = self._db,
)

  其中, self._user丢失,原因貌似由于多线程(至少有两个线程执行中间件的process_response方法),导致在实际写入数据的时候,self._user还没赋值,虽然逻辑上是先赋值给revision_context_manager._user(middleware.py 33~36行),后执行此段代码。鉴于这点,还需要修改middleware.py(C:\Python27\Lib\site-packages\django_reversion-1.8.4-py2.7.egg\reversion\middleware.py)的类RevisionMiddleware的_close_revision方法,其原码如下:

    def _close_revision(self, request):
"""Closes the revision."""
if request.META.get(REVISION_MIDDLEWARE_FLAG, False):
del request.META[REVISION_MIDDLEWARE_FLAG]
revision_context_manager.end()

  新代码:

middleware.py(24~28)

    def _close_revision(self, request):
"""Closes the revision."""
if request.META.get(REVISION_MIDDLEWARE_FLAG, False):
del request.META[REVISION_MIDDLEWARE_FLAG]
revision_context_manager.end(request=request)

即:将request传入到reversion.py的RevisionContextManager类的end方法。

reversion.py(171~182)

                        manager.save_revision(
dict(
(obj, callable(data) and data() or data)
for obj, data
in manager_context.items()
),
user = self._user or (hasattr(request, "user") and request.user),
comment = self._comment,
meta = self._meta,
ignore_duplicates = self._ignore_duplicates,
db = self._db,
)

即:重写user的赋值逻辑。

reversion.py(162行),函数定义改为:

def end(self,request=None):

即增加一个参数: request

django revision的更多相关文章

  1. Django 2.0.1 官方文档翻译: 文档目录 (Page 1)

    Django documentation contents 翻译完成后会做标记. 文档按照官方提供的内容一页一页的进行翻译,有些内容涉及到其他节的内容,会慢慢补上.所有的翻译内容按自己的理解来写,尽量 ...

  2. django后台导入excel文件

    1.django 如何从后台上传excel中批量解析数据 要从django后台导入的excel中批量解析数据,举一个例子,我们向后抬批量导入svn历史数据数据格式 假设excel表中有4列,每列分别是 ...

  3. Python使用Django创建第一个项目

    一 必要环境安装 1首先确保安装了Python3,在此使用的系统为Ubuntu @ubuntu:~$ python3 Python 3.6.7 (default, Oct 22 2018, 11:32 ...

  4. 异步任务队列Celery在Django中的使用

    前段时间在Django Web平台开发中,碰到一些请求执行的任务时间较长(几分钟),为了加快用户的响应时间,因此决定采用异步任务的方式在后台执行这些任务.在同事的指引下接触了Celery这个异步任务队 ...

  5. 《Django By Example》第四章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:祝大家新年快乐,这次带来<D ...

  6. django server之间通过remote user 相互调用

    首先,场景是这样的:存在两个django web应用,并且两个应用存在一定的联系.某些情况下彼此需要获取对方的数据. 但是我们的应用肯经都会有对应的鉴权机制.不会让人家随随便便就访问的对吧.好比上车要 ...

  7. Mysql事务探索及其在Django中的实践(二)

    继上一篇<Mysql事务探索及其在Django中的实践(一)>交代完问题的背景和Mysql事务基础后,这一篇主要想介绍一下事务在Django中的使用以及实际应用给我们带来的效率提升. 首先 ...

  8. Mysql事务探索及其在Django中的实践(一)

    前言 很早就有想开始写博客的想法,一方面是对自己近期所学知识的一些总结.沉淀,方便以后对过去的知识进行梳理.追溯,一方面也希望能通过博客来认识更多相同技术圈的朋友.所幸近期通过了博客园的申请,那么今天 ...

  9. 《Django By Example》第三章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:第三章滚烫出炉,大家请不要吐槽文中 ...

随机推荐

  1. GCD 深入理解:第一部分

    虽然 GCD 已经出现过一段时间了,但不是每个人都明了其主要内容.这是可以理解的:并发一直很棘手,而 GCD 是基于 C 的 API ,它们就像一组尖锐的棱角戳进 Objective-C 的平滑世界. ...

  2. hdu2594 KMP

    2个字符长合并在一起即可.要注意next[n]的值要小于初始的两个字符串的长度; //next[]存的是之前相同的长度. //也是位置,只是s[i]不一定和s[next[i]]相同 //但是i之前的和 ...

  3. SQLServer用sql语句怎么返回一个月所有的天数

    可用如下sql语句: select convert(varchar(10),dateadd(DAY,t2.number,t1.day),120) day from (select '2015-07'+ ...

  4. 洛谷P2015 二叉苹果树

    题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端连接的结点的编号来 ...

  5. bzoj1069 SCOI2007 最大土地面积

    1069: [SCOI2007]最大土地面积 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 2560  Solved: 983 Description ...

  6. MySQL中concat函数

    MySQL中concat函数使用方法:CONCAT(str1,str2,…) 返回结果为连接参数产生的字符串.如有任何一个参数为NULL ,则返回值为 NULL. 注意:如果所有参数均为非二进制字符串 ...

  7. 安装make命令

    步骤1:通过root用户将两个iso源上传到被测试服务器的/opt/huawei/software/iso目录下. # mkdir -p /opt/huawei/software/iso 挂载iso源 ...

  8. Jquery的初识

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. 严格遵守“第一级DOM”能够让你避免与兼容性有关的任何问题

    1级DOM:1级DOM在1998年10月份成为W3C的提议,由DOM核心与DOM HTML两个模块组成.DOM核心能映射以XML为基础的文档结构,允许获取和操作文档的任意部分.DOM HTML通过添加 ...

  10. web.xml文件中的web-app元素 部署

    [转载]web.xml文件中的web-app元素 (2012-05-24 13:35:57) 转载▼ 标签: 转载 分类: java 挺全 的 呵呵呵 转了 原文地址:web.xml文件中的web-a ...