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)

你可能感兴趣的:(Models and databases 之三 自定义Fileds)