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)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容