大部分情况下我们使用Django的标准字段(field)就可以满足一般需求,但是有时不能满足特定需求。Django的内置字段也没有覆盖数据库的全部字段,只是一些常用字段。
- Python对象直接序列化适应数据库的字段
- Field子类化
Python对象直接序列化适应数据库的字段
class Hand(object):
def __init__(self,north,east,south,west):
self.north = north
self.east = east
self.south = south
self.west = west
以上是一个普通的Python类,假设在model中有一个hand属性,同时hand属性时一个Hand的实例。
>>>example = MyModel.objects.get(pk=1)
>>>print(example.hand.north)
>>>new_hand = Hand(north,east,south,west)
>>>example.hand = new_hand
>>>exmple.save()
Field子类化
Django的Field都是继承django.db.models.Field。在自定义Field之前,找到你想要的Field与Django内置的哪个Field相似。
from django.db import models
class HandField(model.Field):
description = "A hand of cards (bridge style)"
def __init__(self,*args,**kwargs):
kwargs['max_length']= 104
super(HandField,self).\_\_init__(*args,**kwargs)
def deconstruct(self):
name,path,args,kwargs = super(HandField,self).deconstruct()
del kwargs['max_length'] #处理添加的信息
return name,path,args,kwargs
HandField继承了大多数的字段选项,只是重写了max_length选项以适应52张牌的
改变自定义字段的类型名,继承基类之后,重写db_type()方法
比如时间在PostgreSQL中叫做 timestamp,而在MySQL是 datetime
from django.db import models
class MytypeField(models.Field):
def db_type(self,connection):
if connection.settings_dict['ENGINE']=='django.db.backends.mysql':
return 'datetime'
else:
return 'timestamp'
#return 'mytype'
db_type() and rel_db_type()方法在创建表和查找相关字段的时候被调用。(rel_db_type()当字段被当做外键或者一对一关系的时候调用)
当期望的数据结构不是基本类型(string,dates,integers,floats)的时候需要重写from_db_value() and to_python()。
- from_db_value()
当数据重数据库中被加载的时候调用,即查询的时候。函数用于转化数据库中的字符到 Python的变量 - to_python()
需要反序列化和表单中调用clean()方法的时候调用。把数据编程Python对象。to_python需要处理的是 正确的对象,字符串和None,需要判断。 函数用于转化数据库中的字符到 Python的变量
import re
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import ugettext_lazy as _
def parse_hand(hand_string):
p1 = re.compile('.{26}')
p2 = re.compile('..')
args = [p2.findall(x) for x in p1.findall(hand_string)]
if len(args) != 4
raise ValidationError(_("Invalid input for a Hand instance"))
return Hand(*args)
class HandField(models.Field):
def from_db_value(self,value,expresson,connection,context):
if value is None:
return value
return parse_hand(value)
def to_python(slef,value):
if isinstance(value,Hand):return value
if value is None:
return value
return parse_hand(value)
def get_prep_value(self, value): #将Python对象转换为查询值
return ''.join([''.join(l) for l in (value.north,value.east, value.south, value.west)])
class Field 是一个抽象类,其子类表示数据表中的一列(字段)。把Python对象映射成数据库里的字段,反之亦然。
- description类属性表示类的描述
从Field到数据库中的字段,数据库提供了一下方法 - get_internal_type()
返回一个字符串,已表明相应的在数据库中的类型,通常是Field的类名
def get_internal_type(self): return 'CharField'
- db_type(connection)
结合连接的数据(MySQL),返回此Field在数据表中数据类型 - rel_db_type(connection)
与db_type(connection)功能类似,只不过在字段充当外键的时候用
There are three main situations where Django needs to interact with the database backend and fields:
•when it queries the database (Python value -> database backend value)
•when it loads data from the database (database backend value -> Python value)
•when it saves to the database (Python value -> database backend value)
get_prep_value(value)
value表示当时model相应属性的值。返回一个能够用于查询参数的格式化数据。
If your custom field uses the CHAR, VARCHAR or TEXT types for MySQL, you must make sure that get_prep_value() always returns a string type.get_db_prep_value(value, connection, prepared=False)
某些数据需要有特定格式存储于数据库中,get_db_prep_value用于装换相应的格式. 二进制格式转化
def get_db_prep_value(self, value, connection, prepared=False):
value = super(BinaryField, self).get_db_prep_value(value, connection, prepared)
if value is not None:
return connection.Database.Binary(value)
return value
from_db_value(value, expression, connection, context)
把从数据库返回的数据 转换成Python变量。是 get_prep_value()反向操作to_python(value)
把value转换成一个Python对象。与value_to_string(obj)是相反的操作。model调用clean()时候调用value_to_string(obj)
把一个对象转成字符串,序列化对象的值
def value_to_string(self, obj):
value = self.value_from_object(obj)
return self.get_prep_value(value)
下面是一个例子来自 自强学院
from django.db import models
import ast
class ListField(models.TextField):
__metaclass__ = models.SubfieldBase
description = "Stores a python list"
def __init__(self, *args, **kwargs):
super(ListField, self).__init__(*args, **kwargs)
def to_python(self, value):
if not value:
value = []
if isinstance(value, list):
return value
return ast.literal_eval(value)
def get_prep_value(self, value):
if value is None:
return value
return str(value)
def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
return self.get_db_prep_value(value)