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求列表交集的方法汇总
Nov 10 Python
Python常用模块介绍
Nov 21 Python
Python实现CET查分的方法
Mar 10 Python
Python进程间通信用法实例
Jun 04 Python
python使用PyCharm进行远程开发和调试
Nov 02 Python
Python基于hashlib模块的文件MD5一致性加密验证示例
Feb 10 Python
Java与Python两大幸存者谁更胜一筹呢
Apr 12 Python
详解Python中的四种队列
May 21 Python
详解如何将python3.6软件的py文件打包成exe程序
Oct 09 Python
对python实现模板生成脚本的方法详解
Jan 30 Python
Python中类的创建和实例化操作示例
Feb 27 Python
python tkinter Entry控件的焦点移动操作
May 22 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调用MsSQL Server 2012存储过程获取多结果集(包含output参数)的详解
2013/07/03 PHP
php控制文件下载速度的方法
2015/03/24 PHP
js 鼠标拖动对象 可让任何div实现拖动效果
2009/11/09 Javascript
JQuery与iframe交互实现代码
2009/12/24 Javascript
JavaScript获取多个数组的交集简单实例
2013/11/11 Javascript
js使用栈来实现10进制转8进制与取除数及余数
2014/06/11 Javascript
JavaScript实现文字与图片拖拽效果的方法
2015/02/16 Javascript
Jquery ajax 同步阻塞引起的UI线程阻塞问题
2015/11/17 Javascript
JS对HTML表格进行增删改操作
2016/08/22 Javascript
Js自动截取字符串长度,添加省略号(……)的实现方法
2017/03/06 Javascript
使用AngularJS对表单提交内容进行验证的操作方法
2017/07/12 Javascript
浅谈node模块与npm包管理工具
2018/01/03 Javascript
JS面向对象的程序设计相关知识小结
2018/05/26 Javascript
jQuery轮播图实例详解
2018/08/15 jQuery
vue tab滚动到一定高度,固定在顶部,点击tab切换不同的内容操作
2020/07/22 Javascript
[06:25]DOTA2英雄梦之声_第17期_大地之灵
2014/06/20 DOTA
[01:58]最残酷竞争 2016国际邀请赛中国区预选赛积分循环赛回顾
2016/06/28 DOTA
python字符串str和字节数组相互转化方法
2017/03/18 Python
python中实现精确的浮点数运算详解
2017/11/02 Python
Python基础练习之用户登录实现代码分享
2017/11/08 Python
python2 与python3的print区别小结
2018/01/16 Python
解决Pycharm出现的部分快捷键无效问题
2018/10/22 Python
浅谈python在提示符下使用open打开文件失败的原因及解决方法
2018/11/30 Python
Python3爬虫全国地址信息
2019/01/05 Python
在Python中使用MySQL--PyMySQL的基本使用方法
2019/11/19 Python
提升python处理速度原理及方法实例
2019/12/25 Python
基于python实现坦克大战游戏
2020/10/27 Python
使用OpenCV实现人脸图像卡通化的示例代码
2021/01/15 Python
Bandier官网:奢侈、时尚前卫的健身服装首选目的地
2020/07/05 全球购物
求职者简历中的自我评价
2013/10/20 职场文书
面试求职的个人自我评价
2013/11/16 职场文书
酒吧员工的岗位职责
2013/11/26 职场文书
网络程序员自荐信
2014/01/25 职场文书
理发店策划方案
2014/06/05 职场文书
2019邀请函格式及范文
2019/05/20 职场文书
2019年中学生的思想品德评语集锦
2019/12/19 职场文书