一、需求背景:
django的models模塊提供了很多數據字段的數據類型field,但是總有寫奇葩需求不能依靠默認字段滿足,所以需要自定義數據數據庫數據字段類型。所有的自定義field應該在app路徑下的fields.py文件里面定義。
二、自定義數據庫數據類型字段:
1、知識點1->繼承自filed類型
所以所有自定義類型都應該如下編寫:
1 from django.db import models 2 3 class myfiled(models.Filed): 4 pass
第一行引入繼承的父類所在的models模塊,然后定義自定義field類型,定義時候繼承models.field類。
2、可以創建一個描述變量
1 from django.db import models 2 3 class myfield(models.Field): 4 description = "This is my own field!"
3、初始化實例的構造函數
1 class myfield(models.Field): 2 def __init__(self,*args,**kwargs): 3 kwargs['max_length'] = 100 4 super(myfield,self).__init__(*args,**kwargs)
這個函數可以不顯性定義
4、對於1.7之前版本的django的自定義field遷移到1.7以及之后的版本,需要自定義deconsturct函數,但這里不在本文的介紹訪問之內。
5、實現db_type方法,作用是返回其本身說屬於的數據庫中的字段數據類型,例如你自定義了一個時間類型,那么對應在MySQL中就是datetime類型。
1 def db_type(self,connection): 2 3 if connection.settings_dict['ENGINE'] == 'django.db.backends.mysql': 4 return 'datetime' 5 else: 6 return 'timestamp'
這里的connection.setting_dict是一個保存着連接配置的字典數據結構,其中ENGINE表示的是數據庫類型,django.db.backends.mysql就是mysql,其實,這里就相當於定義了你自定義數據字段類型說依賴的數據庫數據字段類型,如果對長度要限制,則可以在實例化出事函數中,傳入max_length值給屬性,self.max_length屬性,然后,在db_type里面初始化,完成動態構造。
1 class myfield(models.Field): 2 def __init__(self,max_length,*args,**kwargs): 3 self.max_length = max_length 4 super(myfield,self).__init__(*args,**kwargs) 5 def db_type(self): 6 return 'int(%s)'%self.max_length
6、如果需要自定義某個column,最好還是去數據庫中使用sql語句創建或其他方式,請在db_type函數中返回None,通知migerate創建數據庫表的時候跳過這個字段,因為migerate就是利用這個返回值來創建數據庫表的。當然有些時候也可以使用另外一個函數:get_internal_type
1 def get_internal_type(self): 2 return 'CharField'
7、增刪改查時候需要用到的幾個API:
(1)將值轉換為python對象:
這里有兩個api需要實現from_db_value()和to_python()
引用官方文檔中的一段內容來解釋一下:
如果您的自定義Field類處理比字符串,日期,整數或浮點數更復雜的數據結構,則可能需要覆蓋from_db_value()和to_python()。
如果存在於字段子類,則在從數據庫加載數據(包括在聚合和values()調用)的所有情況下將調用from_db_value()。
to_python()通過反序列化和從表單使用的clean()方法調用。
作為一般規則,to_python()應優雅地處理以下任何參數:
- 正確類型的實例
- 字符串
- None(如果字段允許null=True)
尤其請注意to_python通過反序列化和表單中的clean方法調用,這個要注意反序列化的問題,python的反序列化是一個比較嚴重的安全問題。
所以一般的這兩個函數一般的定義范式:
1 def from_db_value(self,value,expression,connection,content): 2 if value is None: 3 return value 4 try: 5 return field_function(value) 6 except Exception: 7 return value 8 9 def to_python(self,value): 10 if value is None or isinstance(value,xxxx): 11 return value 12 try: 13 return field_function(value) 14 except Exception: 15 return value
當然field_function是對value的處理函數,需要自己定義。
(2)將python對象轉換為查詢值:
當重寫to_python的時候,必須重寫get_prep_value函數,基本上是field_function函數的逆向操作
1 def get_prep_value(self,value): 2 return do_something(value)
(3)將查詢值轉換為數據庫值:
也就是類似像這一類操作的轉換過程(p.s.日期存在數據庫中是unix時間戳,后台處理是2018-01-01 11:07:23.123),都寫在get_db_prep_value函數中
1 def get_db_prep_value(self, value, connection, prepared=False): 2 return do_something(value)
(4)此外還有很多API方法,詳情請參考http://python.usyiyi.cn/documents/django_182/ref/models/fields.html
8、與表單相關可以覆蓋重寫formfield,此處不再展開。
9、關於序列化,這里要展開引一段官方文檔:
轉換字段數據以進行序列化¶
要自定義序列化程序如何序列化值,您可以覆蓋value_to_string()。調用Field._get_val_from_obj(obj)是獲取值序列化的最佳方式。例如,由於我們的HandField使用字符串作為其數據存儲,我們可以重用一些現有的轉換代碼:
class HandField(models.Field): # ... def value_to_string(self, obj): value = self._get_val_from_obj(obj) return self.get_prep_value(value)
這里值得注意。