django中的limit用法(如何在Django中添加没有微秒的 DateTimeField 属性详解)
django中的limit用法
如何在Django中添加没有微秒的 DateTimeField 属性详解前言
今天在项目中遇到一个Django的大坑,一个很简单的分页问题,造成了数据重复。最后排查发现是DateTimeField 属性引起的。
下面描述下问题,下面是我需要用到的一个 Task Model 基本定义:
|
class Task(models.Model): # ...... 省略了其他字段 title = models.CharField(max_length = 256 , verbose_name = u '标题' ) created_at = models.DateTimeField(auto_now_add = True , verbose_name = u '创建时间' ) |
问题描述
前端这边的分页方式不是常规的 page、page_size 方式,而是使用标志位的方式进行分页,我这里采用的就是通过创建时间的时间戳作为分页标记。比如下面是返回的第一页的数据:
|
{ "data" : { "count" : 5 , "has_next" : 1 , "tasks" : [ { "title" : "这是一个作业标题1" , "ts" : 1546829224000 , "id" : 1 }, { "title" : "这是一个作业标题2" , "ts" : 1546829641000 , "id" : 2 } ] }, "result" : 1 } |
要请求第2页的数据只需要在请求的 API 中传递上一页最后一条数据的时间戳即可,这里我们就传递 1546829641000,这样当我后台接收到这个值过后就直接过滤大于该时间戳的数据,再取一页数据返回前端即可,逻辑上很简单。过滤核心代码如下:
|
ts = string_utils.get_num(request.GET.get( 'ts' , 0 ), 0 ) alltask = Task.objects. filter (created_at__gt = date_utils.timestamp2datetime(ts)) |
这段代码很简单,主要就是将前台传递过来的时间戳转换成 DateTime 类型的数据,然后利用created_at__gt来过滤,就是大于这个时间点的就可以。然后问题来了,查询出来的数据始终包含了上一页最后一条数据,感觉很奇怪,我这里明明用的是gt而不是gte,怎么会重复这条数据呢。
于是,我们把上一页最后一条数据的 created_at 字段打印出来和传递过来的时间戳进行对比下:
|
>>> task = Task.objects.get(pk = 2 ) >>> task.created_at datetime.datetime( 2019 , 1 , 7 , 10 , 54 , 1 , 343136 ) |
然后将时间戳转换成 DateTime 类型的数据:
|
>>> ts = int ( 1546829641000 / 1000 ) >>> date_utils.timestamp2datetime(ts) datetime.datetime( 2019 , 1 , 7 , 10 , 54 , 1 ) |
现在看到区别没有,从数据库中查询出来的 created_at 字段的值包含了一个微秒,就是后面的 343136,而时间戳转换成 DateTime 类型的值是不包含这个微秒值的,所以我们上面查询的使用created_at__gt来进行过滤很显然 created_at 的值是大于下面的值的,因为多了一个微秒,所以就造成了数据重复了,终于破案了。
解决方法
那么要怎么解决这个问题呢?当然我们可以直接在数据库中就保存一个时间戳的字段,用这个字段直接来进行查询过滤,肯定是可以解决这个问题的。
如果就用现在的 created_at 这个 DateTimeField 类型呢?如果保存的数据没有这个微秒是不是也可以解决这个问题啊?
我们可以去查看下源码为什么 DateTimeField 类型的数据会包含微秒,下面是django/db/backends/mysql/base.py文件中的部分代码说明:
|
class DatabaseWrapper(BaseDatabaseWrapper): vendor = 'mysql' # This dictionary maps Field objects to their associated MySQL column # types, as strings. Column-type strings can contain format strings; they'll # be interpolated against the values of Field.__dict__ before being output. # If a column type is set to None, it won't be included in the output. _data_types = { 'AutoField' : 'integer AUTO_INCREMENT' , 'BinaryField' : 'longblob' , 'BooleanField' : 'bool' , 'CharField' : 'varchar(%(max_length)s)' , 'CommaSeparatedIntegerField' : 'varchar(%(max_length)s)' , 'DateField' : 'date' , 'DateTimeField' : 'datetime' , 'DecimalField' : 'numeric(%(max_digits)s, %(decimal_places)s)' , 'DurationField' : 'bigint' , 'FileField' : 'varchar(%(max_length)s)' , 'FilePathField' : 'varchar(%(max_length)s)' , 'FloatField' : 'double precision' , 'IntegerField' : 'integer' , 'BigIntegerField' : 'bigint' , 'IPAddressField' : 'char(15)' , 'GenericIPAddressField' : 'char(39)' , 'NullBooleanField' : 'bool' , 'OneToOneField' : 'integer' , 'PositiveIntegerField' : 'integer UNSIGNED' , 'PositiveSmallIntegerField' : 'smallint UNSIGNED' , 'SlugField' : 'varchar(%(max_length)s)' , 'SmallIntegerField' : 'smallint' , 'TextField' : 'longtext' , 'TimeField' : 'time' , 'UUIDField' : 'char(32)' , } @cached_property def data_types( self ): if self .features.supports_microsecond_precision: return dict ( self ._data_types, DateTimeField = 'datetime(6)' , TimeField = 'time(6)' ) else : return self ._data_types # ... further class methods |
上面的 data_types 方法中在进行 MySQL 版本检查,属性supports_microsecond_precision来自于文件django/db/backends/mysql/features.py:
|
class DatabaseFeatures(BaseDatabaseFeatures): # ... properties and methods def supports_microsecond_precision( self ): # See https://github.com/farcepest/MySQLdb1/issues/24 for the reason # about requiring MySQLdb 1.2.5 return self .connection.mysql_version > = ( 5 , 6 , 4 ) and Database.version_info > = ( 1 , 2 , 5 ) |
从上面代码可以看出如果使用的 MySQL 大于等于 5.6.4 版本,属性DateTimeField会被映射成为数据库中的datetime(6),所以保存的数据就包含了微秒。
在 Django 中暂时没有发现可以针对改配置进行设置的方法,所以我们要想保存的数据不包含微秒,我们这里则可以将上面的data_types属性进行覆盖即可:
|
from django.db.backends.mysql.base import DatabaseWrapper DatabaseWrapper.data_types = DatabaseWrapper._data_types |
将上面的代码放置在合适的地方,比如models.py或者__init__.py或者其他地方,当我们运行 migrations 命令来创建 DateTimeField 列的时候对应在数据库中的字段就被隐射成为了datetime,而不是datetime(6),即使你用的是 5.6.4 版本以上的数据库。
当然要立即解决当前的问题,只需要更改下数据库中的 created_at 字段的类型即可:
|
mysql> ALTER TABLE `task` CHANGE COLUMN `created_at` `created_at` datetime NOT NULL; Query OK, 156 rows affected ( 0.14 sec) Records: 156 Duplicates: 0 Warnings: 0 |
这样数据重复的 BUG 就解决了。
参考链接:https://stackoverflow.com/questions/46539755/how-to-add-datetimefield-in-django-without-microsecond
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对开心学习网的支持。
原文链接:https://www.qikqiak.com/post/how-to-add-datetimefield-in-django-without-microsecond/
- CSS3动画属性animation
- css文本怎么控制边距(css中text-overflow属性与文本截断详解)
- vuejs指令解析(Vue.js中的计算属性、监视属性与生命周期详解)
- js里咋样设置css里面的属性(css不常见属性之pointer-events的使用方法)
- html5的文件类型声明(浅析HTML5中的download属性使用)
- sqlserver栏位说明(SQL Server重置IDENTITY属性种子值操作)
- css中background属性介绍
- html中table怎么用(详解CSS的table-layout属性的用法)
- dedecms标签分类(dedecms list增加noflag属性的方法 实现让列表标签不调用有推荐属性的文章)
- cssborder三角形怎么画(使用CSS的border属性绘制各种几何形状的方法)
- vue中什么时候需要set属性(Vue.set和this.$set使用和区别)
- dedecms调用详解(dedecms文章跳转属性打开后页面空白原因分析及解决)
- php的数据类型约束的好处(PHP中的Iterator迭代对象属性详解)
- javascript的作用域详解精简版(浅谈JavaScript 中的延迟加载属性模式)
- css3弹性盒模型常用属性(CSS3弹性盒模型开发笔记三)
- css基础选择器的语法格式(css -webkit-line-clamp WebKit的CSS扩展WebKit是私有属性)
- 秦海璐一袭旗袍惹人倾心,将高级与淡雅展现的游刃有余(秦海璐一袭旗袍惹人倾心)
- 门外之见 海蛎子味 的表演,能走多远(门外之见海蛎子味)
- 三部冷门谍战剧,第一部2014年拍摄,至今还未播出(三部冷门谍战剧)
- 《金陵秘事》的剧情跌宕起伏 给观众带来的怎样的感官体验(金陵秘事的剧情跌宕起伏)
- 少儿口才表达影响未来一生,50首经典绕口令和孩子玩出聪明大脑(少儿口才表达影响未来一生)
- 玩网游居然让人更友善 很难以让人置信(玩网游居然让人更友善)
热门推荐
- 查看docker 镜像大小(详解六种减小Docker镜像大小的方法)
- SQL中的1=1影响性能吗?
- nodejs怎么查看对象的全部属性(浅谈nodejs中创建cluster)
- docker进入容器的方法(docker容器里面的root权限获取方法)
- django框架全面讲解(Django uwsgi Nginx 的生产环境部署详解)
- 云服务器哪种操作系统好(云服务器选什么操作系统比较好?)
- js脚本语言原理(实例说明js脚本语言和php脚本语言的区别)
- h5打开小程序点允许(html5跳转小程序wx-open-launch-weapp踩坑)
- mysql 分片键规则(MySql8 WITH RECURSIVE递归查询父子集的方法)
- laravel使用教程(基于Laravel5.4版本的基本增删改查操作方法)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9