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开启多个子进程并行运行的方法
Apr 18 Python
Python的collections模块中namedtuple结构使用示例
Jul 07 Python
Python中.py文件打包成exe可执行文件详解
Mar 22 Python
windows下安装Python的XlsxWriter模块方法
May 03 Python
Django基础知识与基本应用入门教程
Jul 20 Python
Django中使用第三方登录的示例代码
Aug 20 Python
深入理解Python中的 __new__ 和 __init__及区别介绍
Sep 17 Python
django解决跨域请求的问题详解
Jan 20 Python
Pandas DataFrame数据的更改、插入新增的列和行的方法
Jun 25 Python
使用python模拟命令行终端的示例
Aug 13 Python
python库matplotlib绘制坐标图
Oct 18 Python
python GUI库图形界面开发之PyQt5不规则窗口实现与显示GIF动画的详细方法与实例
Mar 09 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
PHP冒泡排序算法代码详细解读
2011/07/17 PHP
apache+codeigniter 通过.htcaccess做动态二级域名解析
2012/07/01 PHP
在PHP中设置、使用、删除Cookie的解决方法
2013/05/06 PHP
web server使用php生成web页面的三种方法总结
2013/10/28 PHP
PHP中each与list用法分析
2016/01/08 PHP
详解PHP归并排序的实现
2016/10/18 PHP
php微信开发之谷歌测距
2018/06/14 PHP
JavaScript 实现??打印?理
2007/04/28 Javascript
javascript removeChild 使用注意事项
2009/04/11 Javascript
在JavaScript中获取请求的URL参数[正则]
2010/12/25 Javascript
javascript 内存回收机制理解
2011/01/17 Javascript
javascript时间函数基础介绍
2013/03/28 Javascript
JS获取当前日期时间并定时刷新示例
2021/03/04 Javascript
express的中间件bodyParser详解
2014/12/04 Javascript
javascript实现在网页任意处点左键弹出隐藏菜单的方法
2015/05/13 Javascript
js导出excel文件的简洁方法(推荐)
2016/11/02 Javascript
JS数字千分位格式化实现方法总结
2016/12/16 Javascript
JavaScript中从setTimeout与setInterval到AJAX异步
2017/02/13 Javascript
基于vue.js实现侧边菜单栏
2017/03/20 Javascript
纯js实现的积木(div层)拖动功能示例
2017/07/19 Javascript
Vue.js实现实例搜索应用功能详细代码
2017/08/24 Javascript
jquery无缝图片轮播组件封装
2020/11/25 jQuery
浅谈JavaScript闭包
2019/04/09 Javascript
Python与Redis的连接教程
2015/04/22 Python
详解django中自定义标签和过滤器
2017/07/03 Python
Python探索之自定义实现线程池
2017/10/27 Python
django在保存图像的同时压缩图像示例代码详解
2020/02/11 Python
美国正宗设计师眼镜在线零售商:EYEZZ
2019/03/23 全球购物
专科毕业生求职简历的自我评价
2013/10/12 职场文书
企业宣传口号
2014/06/12 职场文书
判缓刑人员个人思想汇报
2014/10/10 职场文书
2015年入党决心书
2015/02/05 职场文书
2015年学生会部门工作总结
2015/04/21 职场文书
居委会工作总结2015
2015/05/18 职场文书
毕业欢送会致辞
2015/07/29 职场文书
初中地理教学反思
2016/02/19 职场文书