介绍
有两种实现方式:
1,编写一个复杂的 Python 对象,让它以某种方式将数据序列化,以适应某个数据库的列类型;
2,创建一个
这里,我们演示第二种方法。
编写一个 field 的子类
1.编写一个继承自Field类的子类
from django.db import models class HandField(models.Field): def __init__(self, *args, **kwargs): kwargs['max_length'] = 104 super(HandField, self).__init__(*args, **kwargs)
我们的
- verbose_name
- name
- primary_key
- max_length
- unique
- blank
- null
- db_index
-
rel: 用于关联字段 (类似
ForeignKey )。仅适用于高级应用。 - default
- editable
-
serialize: 如果为
False,当前 model 传递给 Django 的 serializers 时,就不会被序列化。默认为 True。 - unique_for_date
- unique_for_month
- unique_for_year
- choices
- help_text
- db_column
- db_tablespace: 目前仅适用于 Oracle 数据库和索引创建。一般来说,你可以忽略该选项。
-
auto_created: 如果为 True ,该字段就会被自动创建,比如类实例中的
OneToOneField 字段,仅适用于高级应用。
上面这些加注解的选项,在自定义字段中所起的作用和在普通字段中一样。详见
SubfieldBase 元类
确保你的字段子类使用了一个特别的元类(metaclass):
例如:
class HandField(models.Field): __metaclass__ = models.SubfieldBase def __init__(self, *args, **kwargs): # ...
这段代码保证
有用的方法(Useful methods)
一理你创建了
定制数据库类型
根据
假设你已经创建了一个 PostgreSQL 自定义类型,称为
from django.db import models class MytypeField(models.Field): def db_type(self): return 'mytype'
一旦你有了
class Person(models.Model): name = models.CharField(max_length=80) gender = models.CharField(max_length=1) something_else = MytypeField()
如果你的目的建立一个数据库通用的应用,你就应该考虑不同数据库中列类型的差异。例如,PostgreSQL 中的日期/时间列类型被称为timestamp,而同样的列在
MySQL 中被称为
class MyDateField(models.Field): def db_type(self): from django.conf import settings if settings.DATABASE_ENGINE == 'mysql': return 'datetime' else: return 'timestamp'
db_type()
某些数据库列类型是允许有参数的,比如
# This is a silly example of hard-coded parameters. class CharMaxlength25Field(models.Field): def db_type(self): return 'char(25)' # In the model: class MyModel(models.Model): # ... my_field = CharMaxlength25Field()
更好的方式是在运行时指定参数值-例如,在类实例化之时。在做到这一点,只要实现
# This is a much more flexible example. class BetterCharField(models.Field): def __init__(self, max_length, *args, **kwargs): self.max_length = max_length super(BetterCharField, self).__init__(*args, **kwargs) def db_type(self): return 'char(%s)' % self.max_length # In the model: class MyModel(models.Model): # ... my_field = BetterCharField(25)
最后,如果你的列在生成时需要很复杂的 SQL ,那就在
将数据库内容转换成 PYTHON 对象
将从数据库/序列器中取得的值转换成 Python 对象。
对于大我数应用来说,后台数据库返回格式正确的数据(比如字符串),默认的实现只返回
如果你的自定义
-
一个合适的类型的实例 (例如,在我们的例子中就是
Hand )。 - 一个字符串(例如,从反序列化中得到).
- 数据根据你使用的列类型所返回的一切数据。
在我们的
要注意该方法总是返回一个
牢记:
将 PYTHON 对象转换成数据库的值(CONVERTING PYTHON OBJECTS TO DATABASE VALUES)
这是
import re class HandField(models.Field): # ... def to_python(self, value): if isinstance(value, Hand): return value # The string case. p1 = re.compile('.{26}') p2 = re.compile('..') args = [p2.findall(x) for x in p1.findall(value)] return Hand(*args)
和上面的方法一样,唯一的不同是该方法只在字段值必须被保存到数据库的时候才被调用。就象它的函式名
class HandField(models.Field): # ... def get_db_prep_value(self, value): return ''.join([''.join(l) for l in (value.north, value.east, value.south, value.west)])
存储前对字段进行预处理
在运行
如果你仅仅想在保存之存对数据库某种方式的预处理,只要重写该方法即可。例如,Django 的
如果你要重写该方法,就必须在最后返回该属性的值。如果你改动了字段值,你也应该更新字段属性。所以对该 model 的引用始终都返回正确的值。
为在数据库筛选而做准备工作
如果
你的方法必须要为处理这些
如果你要实现
你也会想实现该方法来限制在自定义字段类型中所用到的筛选条件。
要注意,对
例如,下面的代码实现了
class HandField(models.Field): # ... def get_db_prep_lookup(self, lookup_type, value): # We only handle 'exact' and 'in'. All others are errors. if lookup_type == 'exact': return [self.get_db_prep_value(value)] elif lookup_type == 'in': return [self.get_db_prep_value(v) for v in value] else: raise TypeError('Lookup type %r not supported.' % lookup_type)
为 MODEL 字段指定表单字段
在某字段在 model 中显示时,该方法返回字段默认的表单字段。我们通过
所有的
继续回到我们的例子中,我们可以编写一个
class HandField(models.Field): # ... def formfield(self, **kwargs): # This is a fairly standard way to set up some defaults # while letting the caller override them. defaults = {'form_class': MyFormField} defaults.update(kwargs) return super(HandField, self).formfield(**defaults)
这段代码假设我们已引入了一个
自己写一个简单的demo:
class extraInfoDictField(models.TextField): __metaclass__ = models.SubfieldBase def to_python(self,value): if value is None: return {} if isinstance(value,dict): return value return eval(value) def get_db_prep_save(self,value): #django从1.2起为这个方法添加了一个参数 ''' 1.2以上为get_db_prep_save(self, value, connection) ''' if value is None:return str({}) return str(value) #业务 class Business(models.Model): ....... extra_info = extraInfoDictField('额外信息',null=True) addtime = models.DateTimeField(null=True) changetime = models.DateTimeField(null=True)