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深入学习之特殊方法与多范式
Aug 31 Python
Python动刷新抢12306火车票的代码(附源码)
Jan 24 Python
浅谈python中字典append 到list 后值的改变问题
May 04 Python
对Python 数组的切片操作详解
Jul 02 Python
使用python将时间转换为指定的格式方法
Nov 12 Python
Python魔法方法功能与用法简介
Apr 04 Python
python打开windows应用程序的实例
Jun 28 Python
opencv导入头文件时报错#include的解决方法
Jul 31 Python
详解Python 中sys.stdin.readline()的用法
Sep 12 Python
详解Python的爬虫框架 Scrapy
Aug 03 Python
Python采集爬取京东商品信息和评论并存入MySQL
Apr 12 Python
Python中使用tkFileDialog实现文件选择、保存和路径选择
May 20 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
CakePHP去除默认显示的标题及图标的方法
2008/10/22 PHP
php使用PDO获取结果集的方法
2017/02/16 PHP
用于table内容排序
2006/07/21 Javascript
List all the Databases on a SQL Server
2007/06/21 Javascript
ext 同步和异步示例代码
2009/09/18 Javascript
js监听滚动条滚动事件使得某个标签内容始终位于同一位置
2014/01/24 Javascript
js实现使用鼠标拖拽切换图片的方法
2015/05/04 Javascript
jQuery焦点图切换特效代码分享
2015/09/15 Javascript
Bootstrap每天必学之标签页(Tab)插件
2020/08/09 Javascript
easyui validatebox验证
2016/04/29 Javascript
JavaScript中的ajax功能的概念和示例详解
2016/10/17 Javascript
浅谈jquery的html方法里包含特殊字符的处理
2016/11/30 Javascript
jQuery插件版本冲突的处理方法分析
2017/01/16 Javascript
JavaScript脚本语言是什么_动力节点Java学院整理
2017/06/26 Javascript
详解 vue better-scroll滚动插件排坑
2018/02/08 Javascript
Vue.js 无限滚动列表性能优化方案
2019/12/02 Javascript
[02:51]DOTA2 Supermajor小组分组对阵抽签仪式
2018/06/01 DOTA
python集合类型用法分析
2015/04/08 Python
基于python 爬虫爬到含空格的url的处理方法
2018/05/11 Python
从django的中间件直接返回请求的方法
2018/05/30 Python
利用Python将数值型特征进行离散化操作的方法
2018/11/06 Python
python判断链表是否有环的实例代码
2020/01/31 Python
python打包多类型文件的操作方法
2020/09/21 Python
python推导式的使用方法实例
2021/02/28 Python
skyn ICELAND官网:冰岛成分天然护肤品
2020/08/24 全球购物
益模软件Java笔试题
2012/03/27 面试题
介绍一下linux的文件系统
2015/10/06 面试题
建筑工程专业学生的自我评价
2013/12/25 职场文书
自我鉴定书面格式
2014/01/13 职场文书
大学生先进事迹材料
2014/02/16 职场文书
高考励志标语
2014/06/05 职场文书
刑事案件上诉状
2015/05/23 职场文书
诚实守信主题班会
2015/08/13 职场文书
Redis如何一键部署脚本
2021/04/12 Redis
分析MySQL抛出异常的几种常见解决方式
2021/05/18 MySQL
DE1107机评
2022/04/05 无线电