Django自定义YamlField实现过程解析


Posted in Python onNovember 11, 2020

需求

在使用django admin时希望后台的Textarea多行文本框可以按yaml格式编写,数据库保存为Text文本类型,字段和接口中读取出来自动变为字典或列表格式。

试过pip install django-yamlfied,修改支持新版django之后

接口中返回的字段是字符串形式,不符合预期。

之前写过一版。

import yaml
from django.db import models

class YamlField(models.TextField):
  def to_python(self, value): # 将数据库内容转为python对象时调用
    if not value:
      value = {}
    if isinstance(value, (list, dict)):
      return value
    return yaml.safe_load(value)

  def get_prep_value(self, value): # create时插入数据, 转为字符串存储
    return value if value is None else yaml.dump(value, default_flow_style=False)

  def from_db_value(self, value, expression, connection): # 从数据库读取字段是调用
    return self.to_python(value)

问题是输入框输入

- a
- b
- c

保存后就会变成字典的字符串形式

['a','b','c']

无法原样保存,反复研究后,参考django-jsonfield写了一版。

原理是,改为继承models.Field类,(继承models.TextField类,则formfield和value_to_string不生效)

数据库依旧将数据库中的yaml文本转为dict/list,在django admin中通过自定义widget显示为yaml字符串格式。

为了保存时,验证表单中yaml字符串格式是否正确,还需要自定义一个form。完整代码如下。

import django
from django.db import models
from django import forms
from django.core.exceptions import ValidationError
import yaml


class YamlWidget(forms.Textarea):
  def render(self, name, value, attrs=None, renderer=None):
    if value is None:
      value = ""
    if not isinstance(value, str):
      value = yaml.safe_dump(value, default_flow_style=False)
    if django.VERSION < (2, 0):
      return super().render(name, value, attrs)
    return super().render(name, value, attrs, renderer)


class YamlFormField(forms.CharField):
  empty_values = [None, '']

  def __init__(self, *args, **kwargs):
    if 'widget' not in kwargs:
      kwargs['widget'] = YamlWidget
    super().__init__(*args, **kwargs)

  def to_python(self, value):
    if isinstance(value, str) and value:
      try:
        return yaml.safe_load(value)
      except Exception as exc:
        raise forms.ValidationError('Yaml decode error: %s' % (exc.args[0],))
    else:
      return value

  def validate(self, value):
    if value in self.empty_values and self.required:
      raise forms.ValidationError(self.error_messages['required'], code='required')


class YamlField(models.Field):
  description = "Yaml object"

  def get_internal_type(self):
    return 'TextField'

  def formfield(self, **kwargs):
    defaults = {
      'form_class': YamlFormField,
      'widget': YamlWidget
    }
    defaults.update(**kwargs)
    return super().formfield(**defaults)

  def to_python(self, value: str): # 将数据库内容转为python对象时调用
    if value is None:
      if not self.null and self.blank:
        return ""
      return None
    if isinstance(value, (list, dict)):
      return value
    value = yaml.safe_load(value)
    return value

  def validate(self, value, model_instance): # 验证从接受到字典格式
    if not self.null and value is None:
      raise ValidationError(self.error_messages['null'])
    try:
      self.get_prep_value(value)
    except ValueError:
      raise ValidationError(self.error_messages['invalid'] % value)

  def get_prep_value(self, value: (list, dict)): # 保存时插入数据, 转为字符串存储
    if value is None:
      return None
    value = yaml.safe_dump(value, default_flow_style=False)
    return value

  def from_db_value(self, value: str, expression, connection, *args, **kwargs): # 从数据库读取字段是调用
    return self.to_python(value)

  def value_to_string(self, obj): # Rest Framework调用时
    return self.value_from_object(obj)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
使用 Python 获取 Linux 系统信息的代码
Jul 13 Python
python使用线程封装的一个简单定时器类实例
May 16 Python
python显示生日是星期几的方法
May 27 Python
在Django的session中使用User对象的方法
Jul 23 Python
Python 爬虫的工具列表大全
Jan 31 Python
python实现任意位置文件分割的实例
Dec 14 Python
Python3 单行多行万能正则匹配方法
Jan 07 Python
Django组件content-type使用方法详解
Jul 19 Python
在python中做正态性检验示例
Dec 09 Python
Python响应对象text属性乱码解决方案
Mar 31 Python
解决echarts中饼图标签重叠的问题
May 16 Python
python使用建议技巧分享(三)
Aug 18 Python
Python监听剪切板实现方法代码实例
Nov 11 #Python
如何通过python计算圆周率PI
Nov 11 #Python
python中turtle库的简单使用教程
Nov 11 #Python
python 怎样进行内存管理
Nov 10 #Python
python tqdm实现进度条的示例代码
Nov 10 #Python
python 解决Windows平台上路径有空格的问题
Nov 10 #Python
Python在后台自动解压各种压缩文件的实现方法
Nov 10 #Python
You might like
thinkphp模板输出技巧汇总
2014/11/24 PHP
PHP的RSA加密解密方法以及开发接口使用
2018/02/11 PHP
PHP基于cookie实现统计在线人数功能示例
2019/01/16 PHP
Laravel框架查询构造器简单示例
2019/05/08 PHP
JavaScript 原型链学习总结
2010/10/29 Javascript
JavaScript入门之基本函数详解
2011/10/21 Javascript
js中传递特殊字符(+,&amp;)的方法
2014/01/16 Javascript
jQuery ui 利用 datepicker插件实现开始日期(minDate)和结束日期(maxDate)
2014/05/22 Javascript
基于jquery animate操作css样式属性小结
2015/11/27 Javascript
基于BootStarp的Dailog
2016/04/28 Javascript
Angular的$http与$location
2016/12/26 Javascript
jQuery实现贪吃蛇小游戏(附源码下载)
2017/03/04 Javascript
详解Vue CLI 3.0脚手架如何mock数据
2018/11/23 Javascript
其实你可以少写点if else与switch(推荐)
2019/01/10 Javascript
微信小程序实现顶部导航特效
2019/01/28 Javascript
bootstrap table实现横向合并与纵向合并
2019/07/18 Javascript
node解析修改nginx配置文件操作实例分析
2019/11/06 Javascript
浅谈vue-props的default写不写有什么区别
2020/08/09 Javascript
Python处理Excel文件实例代码
2017/06/20 Python
pandas groupby 分组取每组的前几行记录方法
2018/04/20 Python
win8下python3.4安装和环境配置图文教程
2018/07/31 Python
在Python中居然可以定义两个同名通参数的函数
2019/01/31 Python
Django项目使用CircleCI的方法示例
2019/07/14 Python
python批量图片处理简单示例
2019/08/06 Python
python+selenium select下拉选择框定位处理方法
2019/08/24 Python
使用python制作一个解压缩软件
2019/11/13 Python
哥德堡通行证:Gothenburg Pass
2019/12/09 全球购物
Yahoo-PHP面试题1
2016/07/20 面试题
银行会计职员个人的自我评价
2013/09/29 职场文书
新入职员工的自我介绍演讲稿
2014/01/02 职场文书
大学生求职信例文
2014/06/29 职场文书
预备党员转正思想汇报
2014/09/26 职场文书
介绍信的写法
2015/01/31 职场文书
美甲店的创业计划书模板
2019/08/23 职场文书
使用CSS自定义属性实现骨架屏效果
2022/06/21 HTML / CSS
利用Python实现翻译HTML中的文本字符串
2022/06/21 Python