小編給大家分享一下怎么在Django中添加沒有微秒的 DateTimeField屬性詳解,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
公司主營業(yè)務(wù):成都網(wǎng)站制作、成都網(wǎng)站建設(shè)、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴謹、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)推出秦安免費做網(wǎng)站回饋大家。前言
今天在項目中遇到一個Django的大坑,一個很簡單的分頁問題,造成了數(shù)據(jù)重復。最后排查發(fā)現(xiàn)是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'創(chuàng)建時間')
問題描述
前端這邊的分頁方式不是常規(guī)的 page、page_size 方式,而是使用標志位的方式進行分頁,我這里采用的就是通過創(chuàng)建時間的時間戳作為分頁標記。比如下面是返回的第一頁的數(shù)據(jù):
{ "data": { "count": 5, "has_next": 1, "tasks": [ { "title": "這是一個作業(yè)標題1", "ts": 1546829224000, "id": 1 }, { "title": "這是一個作業(yè)標題2", "ts": 1546829641000, "id": 2 } ] }, "result": 1 }
要請求第2頁的數(shù)據(jù)只需要在請求的 API 中傳遞上一頁最后一條數(shù)據(jù)的時間戳即可,這里我們就傳遞 1546829641000,這樣當我后臺接收到這個值過后就直接過濾大于該時間戳的數(shù)據(jù),再取一頁數(shù)據(jù)返回前端即可,邏輯上很簡單。過濾核心代碼如下:
ts = string_utils.get_num(request.GET.get('ts', 0), 0) alltask = Task.objects.filter(created_at__gt=date_utils.timestamp2datetime(ts))
這段代碼很簡單,主要就是將前臺傳遞過來的時間戳轉(zhuǎn)換成 DateTime 類型的數(shù)據(jù),然后利用created_at__gt來過濾,就是大于這個時間點的就可以。然后問題來了,查詢出來的數(shù)據(jù)始終包含了上一頁最后一條數(shù)據(jù),感覺很奇怪,我這里明明用的是gt而不是gte,怎么會重復這條數(shù)據(jù)呢。
于是,我們把上一頁最后一條數(shù)據(jù)的 created_at 字段打印出來和傳遞過來的時間戳進行對比下:
>>> task = Task.objects.get(pk=2) >>> task.created_at datetime.datetime(2019, 1, 7, 10, 54, 1, 343136)
然后將時間戳轉(zhuǎn)換成 DateTime 類型的數(shù)據(jù):
>>> ts = int(1546829641000/1000) >>> date_utils.timestamp2datetime(ts) datetime.datetime(2019, 1, 7, 10, 54, 1)
現(xiàn)在看到區(qū)別沒有,從數(shù)據(jù)庫中查詢出來的 created_at 字段的值包含了一個微秒,就是后面的 343136,而時間戳轉(zhuǎn)換成 DateTime 類型的值是不包含這個微秒值的,所以我們上面查詢的使用created_at__gt來進行過濾很顯然 created_at 的值是大于下面的值的,因為多了一個微秒,所以就造成了數(shù)據(jù)重復了,終于破案了。
解決方法
那么要怎么解決這個問題呢?當然我們可以直接在數(shù)據(jù)庫中就保存一個時間戳的字段,用這個字段直接來進行查詢過濾,肯定是可以解決這個問題的。
如果就用現(xiàn)在的 created_at 這個 DateTimeField 類型呢?如果保存的數(shù)據(jù)沒有這個微秒是不是也可以解決這個問題啊?
我們可以去查看下源碼為什么 DateTimeField 類型的數(shù)據(jù)會包含微秒,下面是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會被映射成為數(shù)據(jù)庫中的datetime(6),所以保存的數(shù)據(jù)就包含了微秒。
在 Django 中暫時沒有發(fā)現(xiàn)可以針對改配置進行設(shè)置的方法,所以我們要想保存的數(shù)據(jù)不包含微秒,我們這里則可以將上面的data_types屬性進行覆蓋即可:
from django.db.backends.mysql.base import DatabaseWrapper DatabaseWrapper.data_types = DatabaseWrapper._data_types
將上面的代碼放置在合適的地方,比如models.py或者__init__.py或者其他地方,當我們運行 migrations 命令來創(chuàng)建 DateTimeField 列的時候?qū)?yīng)在數(shù)據(jù)庫中的字段就被隱射成為了datetime,而不是datetime(6),即使你用的是 5.6.4 版本以上的數(shù)據(jù)庫。
當然要立即解決當前的問題,只需要更改下數(shù)據(jù)庫中的 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
這樣數(shù)據(jù)重復的 BUG 就解決了。
以上是“怎么在Django中添加沒有微秒的 DateTimeField屬性詳解”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)成都網(wǎng)站設(shè)計公司行業(yè)資訊頻道!
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
本文題目:怎么在Django中添加沒有微秒的DateTimeField屬性詳解-創(chuàng)新互聯(lián)
分享鏈接:http://www.muchs.cn/article42/dieghc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站營銷、軟件開發(fā)、建站公司、企業(yè)建站、靜態(tài)網(wǎng)站、電子商務(wù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容