大部分情況下我們使用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)