Django学习笔记第十二篇--关于自定义数据库字段数据类型

时间:2021-05-26 15:41:07

一、需求背景:

django的models模块提供了很多数据字段的数据类型field,但是总有写奇葩需求不能依靠默认字段满足,所以需要自定义数据数据库数据字段类型。所有的自定义field应该在app路径下的fields.py文件里面定义。

二、自定义数据库数据类型字段:

1、知识点1->继承自filed类型

所以所有自定义类型都应该如下编写:

 from django.db import models

 class myfiled(models.Filed):
pass

第一行引入继承的父类所在的models模块,然后定义自定义field类型,定义时候继承models.field类。

2、可以创建一个描述变量

 from django.db import models

 class myfield(models.Field):
description = "This is my own field!"

3、初始化实例的构造函数

 class myfield(models.Field):
def __init__(self,*args,**kwargs):
kwargs['max_length'] = 100
super(myfield,self).__init__(*args,**kwargs)

这个函数可以不显性定义

4、对于1.7之前版本的django的自定义field迁移到1.7以及之后的版本,需要自定义deconsturct函数,但这里不在本文的介绍访问之内。

5、实现db_type方法,作用是返回其本身说属于的数据库中的字段数据类型,例如你自定义了一个时间类型,那么对应在MySQL中就是datetime类型。

 def db_type(self,connection):

     if connection.settings_dict['ENGINE'] == 'django.db.backends.mysql':
return 'datetime'
else:
return 'timestamp'

这里的connection.setting_dict是一个保存着连接配置的字典数据结构,其中ENGINE表示的是数据库类型,django.db.backends.mysql就是mysql,其实,这里就相当于定义了你自定义数据字段类型说依赖的数据库数据字段类型,如果对长度要限制,则可以在实例化出事函数中,传入max_length值给属性,self.max_length属性,然后,在db_type里面初始化,完成动态构造。

 class myfield(models.Field):
def __init__(self,max_length,*args,**kwargs):
self.max_length = max_length
super(myfield,self).__init__(*args,**kwargs)
def db_type(self):
return 'int(%s)'%self.max_length

6、如果需要自定义某个column,最好还是去数据库中使用sql语句创建或其他方式,请在db_type函数中返回None,通知migerate创建数据库表的时候跳过这个字段,因为migerate就是利用这个返回值来创建数据库表的。当然有些时候也可以使用另外一个函数:get_internal_type

 def get_internal_type(self):
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的反序列化是一个比较严重的安全问题。

所以一般的这两个函数一般的定义范式:

 def from_db_value(self,value,expression,connection,content):
if value is None:
return value
try:
return field_function(value)
except Exception:
return value def to_python(self,value):
if value is None or isinstance(value,xxxx):
return value
try:
return field_function(value)
except Exception:
return value

当然field_function是对value的处理函数,需要自己定义。

(2)将python对象转换为查询值:

当重写to_python的时候,必须重写get_prep_value函数,基本上是field_function函数的逆向操作

 def get_prep_value(self,value):
return do_something(value)

(3)将查询值转换为数据库值:

也就是类似像这一类操作的转换过程(p.s.日期存在数据库中是unix时间戳,后台处理是2018-01-01 11:07:23.123),都写在get_db_prep_value函数中

 def get_db_prep_value(self, value, connection, prepared=False):
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)

这里值得注意。