自定义一个Django的Model Filed--StaticJSONField

栏目: Python · 发布时间: 6年前

内容简介:自定义一个Django的Model Filed--StaticJSONField

使用JSON保存用户数据的问题

有时候我们会用json来保存一些杂乱的数据,比如一个用户的各种信息,这些信息被序列化为一个json字符串并最终存放在数据库的一个column里面。

user.config = {
	"accept_notification": True,
	"accept_invitation": True,
	"level": "golden"
}

使用json保存这些配置的好处在于,添加或者修改属性的时候你不需要对数据库进行修改。在关系型数据库看来,这个config不过是一个字符串而已。

但是这样做也会有一些问题,比如:

  • 有时候写了错别字,依然可以正常地保存到数据库: user.config["leel"] = “golden”
  • 删除json里面的字段时需要遍历数据库,将所有的json里面对应字段删除,不然它们会一直占用空间。
  • 获取数据时,往往需要添加一个默认值: user.config.get("level", "golden") ,因为你无法确认level是否一定在config里面。

使用 __getattr__ 以及 __setattr__ 的一个解决方案

__getattr____setattr__python 的魔术方法,用它可以对自定义的类添加一些特殊的效果。

调用instance.atttibute的时候, 如果这个attribute没有明确的在instance所属的类里面明确定义,就会触发对 __getattr__ 的访问。调用instance.attribute = value的时候,必然会命中对 __setattr__ 的访问。基于这两个原理,我们可以编写这么一个类:

class InvalidAttributeException(Exception):
    pass


class StaticJSON(object):
    def __init__(self, default, value=None):
        value = value or {}
        self.__dict__["_default"] = default
        self.__dict__["_value"] = value
        for k, v in self._default.items():
            self.__dict__["_value"].setdefault(k, v)

    def __getattr__(self, name):
        if name in self.__dict__.get("_default", {}):
            return self.__dict__["_value"][name]
        else:
            raise AttributeError(name)

    def __setattr__(self, name, value):
        if name == "_default" or name == "_value":
            setattr(self.__dict__[name], name, value)

        if name not in self.__dict__["_default"]:
            raise InvalidAttributeException("{} is not a valid attribute, try {} instead".format(
                name, ", ".join(self.__dict__["_default"].keys())
            ))
        self.__dict__["_value"][name] = value

    def items(self):
        """ used for serializer.to_representation
        """
        return self._value.items()

    def __iter__(self):
        """ for i in StaticJSON:
                pass
        """
        for item in self._value:
            yield item

看着上面代码有一点头晕,这里我们举一个例子,我们可以这么使用这个类:

# 用户的默认属性
default= {
    "accept_invitation": True,
    "accept_notification": False,
    "level": "golden"
}
# 用户的属性
value = {
    "level": "diamond"
}
config = StaticJSON(default=default, value=value)
# 因为value里面明确定义了level是diamond,因此这边获取到的是value的值
config.level
>>>diamond
config.level = "golden"
# 用户没有定义accept_notification 属性,因此返回的是默认值
config.accept_notification
>>> False

# 调用未定义的属性, 报错
config.non_exist_attribute = "some attribute"
>>> Traceback (most recent call last):
  File "<input>", line 1, in <module>
    config.non_exist_attribute = "some attribute"
  File "static_json.py", line 34, in __setattr__
    raise InvalidAttributeException("{} is not a valid attribute, try {} instead".format(
__console__.InvalidAttributeException: non_exist_attribute is not a valid attribute, try accept_invitation, accept_notification, level instead

#  像字典一样对这个instance迭代:
for key in config:
	print(key, getattr(config, key))
	
for key,value in config.items():
	print(key, value)

总而言之,我们做了这几件事情:

  1. __init__ 函数接受默认值(default)以及实际值(value),并将这些数据分别保存到 <StaticJSON instance>._default 以及 <StaticJSON instance>._value 里面. 如果传入的default里面有一些attribute在value里面不存在,那么就用default去填充它。
  2. 编写 __getattr__ 方法,这个方法使得在获取值的时候( <StaticJSON instance>.attribute ), 实际从 <StaticJSON instance>._value 里面获取,如果找不到, 说明这个attribute不应该被访问,报错。
  3. 编写 __setattr__ 方法,在赋值的时候( <StaticJSON instance>.attribute = value ), 先检查这个attribute是否符合被允许, 然后保存到 <StaticJSON instance>._value 里面。
  4. 编写 _iter_ 以及 items 方法吗,因此我们可以使用类似 for key,value in instance.items(): 或者 for key in instance 两种方法对这个instance进行迭, 其实也就是让 <StaticJSON instance> 表现的更加像字典。

为了让这个类更加像字典, 我们还可以实现 __getitem__ , __setitem__ 方法。

实现一个Django的自定义Field

上面的 StaticJSON 可以用来对数据进行修改,查询,但是不能够被直接定义到Djanog的model里面,为此我们需要实现一个StaticJSONField,具体的实现代码在 gist 里面。

一般来讲我们可以这样定义一个model:

from somewhere import StaticJSONField

class User(Model):
	DEFAULT_CONFIG = {
		"accept_invitation": True,
		"accept_notification": False
		"level": "golden"
 }
 name = models.CharField(max_length=20)
 config = StaticJSONField(default=DEFAULT_SUMMARY)


print(user.config.level)
user.config.level = "diamond"
user.config.accept_notification = False

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

用户思维+:好产品让用户为自己尖叫

用户思维+:好产品让用户为自己尖叫

[美] Kathy Sierra / 石航 / 人民邮电出版社 / 2017-9 / 69.00元

畅销产品与普通产品的本质区别是什么?若没有巨额预算、不爱营销噱头、不开奢华的产品发布会,如何打造可持续成功的产品?本书针对上述问题提出了新颖的观点:用户并不关心产品本身有多棒,而是关心使用产品时自己有多棒。作者利用其多年的交互设计经验,生动阐释了这一观点背后的科学。可贵的是,本书并不止步于解释“为什么”,还清晰呈现了“怎么做”。 本书风格活泼、图文并茂,其对话式内容既引人入胜,又引人深思,适......一起来看看 《用户思维+:好产品让用户为自己尖叫》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具