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

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. oracle-2-sql数据操作和查询

    主要内容: >oracle 数据类型 >sql建表和约束 >sql对数九的增删改 >sql查询 >oracle伪例 1.oracle的数据类型 oracle数据库的核心是 ...

  2. 【BZOJ 3545】【ONTAK 2010】Peaks & 【BZOJ 3551】【ONTAK 2010】Peaks加强版 Kruskal重构树

    sunshine的A题我竟然调了一周!!! 把循环dfs改成一个dfs就可以,,,我也不知道为什么这样就不会RE,但它却是A了,,, 这周我一直在调这个题,总结一下智障错误: 1.倍增的范围设成了n而 ...

  3. maven中Rhino classes (js.jar) not found - Javascript disabled的处理

    想使用单元测试 来测一下服务请求,于是想到了使用Junit,查了一下,决定使用 HttpUnit 来发送请求 于是在maven中引入了 <dependency> <groupId&g ...

  4. Java 并发-任务执行.

    首先来看一下,任务的定义: 所谓的任务,就是抽象,离散的工作单位.你可以简单理解为代码级别的 (Runnable接口) 大多数并发应用程序都是围绕着任务进行管理的. 看一小段代码: package c ...

  5. 在java类中,是先执行类的构造函数还是先执行类的私有非静态变量

    举例子: package test_instance; public class TestClassLoaderTime { public TestClassLoaderTime(){ System. ...

  6. C语言浮点数除法可以精确到多少位小数

    double型的两个数相除,得到的浮点数能精确到多少位呢..用我家电脑做了个实验,编译器是Code::Blocks 13.12. 然后用电脑自带的计算器算的结果和C语言算的结果比较如图. 第一例里a= ...

  7. 2016年4月7日 js的全选和反选

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title> ...

  8. jquery------使用jQuery的委托方法

    index.jsp <div id="gallery"> <div class="photo"> <img src="a ...

  9. 锋利的jQuery-4--给事件添加命名空间

    可以把为元素绑定的多个事件用命名空间规范起来. $(function(){ $("p").bind("mouseover.plugin", function() ...

  10. SQL注入原理小结

    今天,一基友问我一个问题说:为什么SQL注入要加单引号,这个当时我一时也回答不上,怪就怪自己理论太菜,不过回去仔细思考了一下,觉得这个问题也是蛮简单的. 首先大家应该明白的一点就是SQL注入的目的:加 ...