Models and databases 之三 自定義Fileds

大部分情況下我們使用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)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容