来自 编程应用 2019-09-04 19:46 的文章
当前位置: 六合联盟网 > 编程应用 > 正文

django自定义Field落成三个字段存款和储蓄以逗号分

实现了在一个字段存储以逗号分隔的字符串,返回一个相应的列表

大部分情况下我们使用Django的标准字段(field)就可以满足一般需求,但是有时不能满足特定需求。Django的内置字段也没有覆盖数据库的全部字段,只是一些常用字段。

复制代码 代码如下:

<li>Python对象直接序列化适应数据库的字段
<li>Field子类化

from django import forms
from django.db import models
from django.utils.text import capfirst
from django.core import exceptions

Python对象直接序列化适应数据库的字段

<pre>
class Hand(object):
def __init__(self,north,east,south,west):
self.north = north
self.east = east
self.south = south
self.west = west
</pre>
以上是一个普通的Python类,假设在model中有一个hand属性,同时hand属性时一个Hand的实例。
<pre>

example = MyModel.objects.get(pk=1)
print(example.hand.north)
new_hand = Hand(north,east,south,west)
example.hand = new_hand
exmple.save()
</pre>

class MultiSelectFormField(forms.MultipleChoiceField):
    widget = forms.CheckboxSelectMultiple

Field子类化

Django的Field都是继承django.db.models.Field。在自定义Field之前,找到你想要的Field与Django内置的哪个Field相似。

<pre>
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
</pre>
HandField继承了大多数的字段选项,只是重写了max_length选项以适应52张牌的

改变自定义字段的类型名,继承基类之后,重写db_type()方法
比如时间在PostgreSQL中叫做 timestamp,而在MySQL是 datetime
<pre>
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'
</pre>
db_type() and rel_db_type()方法在创建表和查找相关字段的时候被调用。(rel_db_type()当字段被当做外键或者一对一关系的时候调用)

当期望的数据结构不是基本类型(string,dates,integers,floats)的时候需要重写from_db_value() and to_python()。
<li>from_db_value()
当数据重数据库中被加载的时候调用,即查询的时候。函数用于转化数据库中的字符到 Python的变量
<li>to_python()
需要反序列化和表单中调用clean()方法的时候调用。把数据编程Python对象。to_python需要处理的是 正确的对象,字符串和None,需要判断。 函数用于转化数据库中的字符到 Python的变量
<pre>
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)])
</pre>


class Field 是一个抽象类,其子类表示数据表中的一列(字段)。把Python对象映射成数据库里的字段,反之亦然。

<li>description类属性表示类的描述
从Field到数据库中的字段,数据库提供了一下方法
<li>get_internal_type()
返回一个字符串,已表明相应的在数据库中的类型,通常是Field的类名
<pre>
def get_internal_type(self): return 'CharField'
</pre>
<li>db_type(connection)
结合连接的数据(MySQL),返回此Field在数据表中数据类型
<li>rel_db_type(connection)
与db_type(connection)功能类似,只不过在字段充当外键的时候用
<pre>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)
</pre>

<li>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.

<li>get_db_prep_value(value, connection, prepared=False)
某些数据需要有特定格式存储于数据库中,get_db_prep_value用于装换相应的格式. 二进制格式转化
<pre>
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
</pre>

<li>from_db_value(value, expression, connection, context)
把从数据库返回的数据 转换成Python变量。是 get_prep_value()反向操作

<li>to_python(value)
把value转换成一个Python对象。与value_to_string(obj)是相反的操作。model调用clean()时候调用
<li>value_to_string(obj)
把一个对象转成字符串,序列化对象的值
<pre>
def value_to_string(self, obj):
value = self.value_from_object(obj)
return self.get_prep_value(value)
</pre>

下面是一个例子来自 自强学院

<pre>
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)

</pre>

    def __init__(self, *args, **kwargs):
        self.max_choices = kwargs.pop('max_choices', 0)
        super(MultiSelectFormField, self).__init__(*args, **kwargs)

    def clean(self, value):
        if not value and self.required:
            raise forms.ValidationError(self.error_messages['required'])
        # if value and self.max_choices and len(value) > self.max_choices:
        #     raise forms.ValidationError('You must select a maximum of %s choice%s.'
        #             % (apnumber(self.max_choices), pluralize(self.max_choices)))
        return value

class MultiSelectField(models.Field):
    __metaclass__ = models.SubfieldBase

    def get_internal_type(self):
        return "CharField"

    def get_choices_default(self):
        return self.get_choices(include_blank=False)

    def _get_FIELD_display(self, field):
        value = getattr(self, field.attname)
        choicedict = dict(field.choices)

    def formfield(self, **kwargs):
        # don't call super, as that overrides default widget if it has choices
        defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name),
                    'help_text': self.help_text, 'choices': self.choices}
        if self.has_default():
            defaults['initial'] = self.get_default()
        defaults.update(kwargs)
        return MultiSelectFormField(**defaults)

    def get_prep_value(self, value):
        return value

    def get_db_prep_value(self, value, connection=None, prepared=False):
        if isinstance(value, basestring):
            return value
        elif isinstance(value, list):
            return ",".join(value)

    def to_python(self, value):
        if value is not None:
            return value if isinstance(value, list) else value.split(',')
        return ''

    def contribute_to_class(self, cls, name):
        super(MultiSelectField, self).contribute_to_class(cls, name)
        if self.choices:
            func = lambda self, fieldname = name, choicedict = dict(self.choices): ",".join([choicedict.get(value, value) for value in getattr(self, fieldname)])
            setattr(cls, 'get_%s_display' % self.name, func)

    def validate(self, value, model_instance):
        arr_choices = self.get_choices_selected(self.get_choices_default())
        for opt_select in value:
            if (int(opt_select) not in arr_choices):  # the int() here is for comparing with integer choices
                raise exceptions.ValidationError(self.error_messages['invalid_choice'] % value)
        return

    def get_choices_selected(self, arr_choices=''):
        if not arr_choices:
            return False
        list = []
        for choice_selected in arr_choices:
            list.append(choice_selected[0])
        return list

    def value_to_string(self, obj):
        value = self._get_val_from_obj(obj)
        return self.get_db_prep_value(value)

复制代码 代码如下: from django import forms from django.db import models from django...

本文由六合联盟网发布于编程应用,转载请注明出处:django自定义Field落成三个字段存款和储蓄以逗号分

关键词: