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

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

内容简介:自定义一个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

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

查看所有标签

猜你喜欢:

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

技术元素

技术元素

[美] 凯文·凯利 / 张行舟、余倩、周峰、管策、金鑫、曾丹阳、李远、袁璐 / 译言·东西文库/电子工业出版社 / 2012-5 / 55.00元

我会将我不成熟的想法、笔记、内心争论、草稿以及对其他文章的回应都写在《技术元素》中,这样我就能知道自己到底在想些什么。——KK “技术元素”(technium)是凯文•凯利专门创造出来的词语。“技术元素不仅仅包括一些具象的技术(例如汽车、雷达和计算机等),它还包括文化、 法律、社会机构和所有的智能创造物。”简而言之,技术元素就是从人的意识中涌现出来的一切。KK把这种科技的延伸面看成一个能产生......一起来看看 《技术元素》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具