• Django自定义YamlField


    需求

    在使用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)
    
    
  • 相关阅读:
    TWaver网元动态转动效果
    替换TWaver中Tree展开合并图标
    MOSS 2010:Visual Studio 2010开发体验(10)——列表开发之内容类型
    MOSS 2010:Visual Studio 2010开发体验(8)——Silverlight应用
    MOSS 2010:Visual Studio 2010开发体验(13)——列表开发之列表实例
    MOSS 2010:Visual Studio 2010开发体验(5)——Mapped Folder
    MOSS 2010:Visual Studio 2010开发体验(6)——开发WebPart
    MOSS 2010:Visual Studio 2010开发体验(7)——AJAX Web Part
    MOSS 2010:Visual Studio 2010开发体验(11)——扩展SharePoint Explorer
    MOSS 2010:Visual Studio 2010开发体验(12)——列表开发之列表定义
  • 原文地址:https://www.cnblogs.com/superhin/p/13860557.html
Copyright © 2020-2023  润新知