美文网首页
Django自定义小部件(widget)

Django自定义小部件(widget)

作者: 黑色汪汪汪 | 来源:发表于2018-09-04 10:21 被阅读0次

在啃了官网文档之后依旧不明白的同学,应该都知道,官网的文档对这方面写的挺笼统的,https://docs.djangoproject.com/zh-hans/2.1/ref/forms/widgets/#styling-widget-instances
这是官网的文档,在后面的“MultiWidget”里,有个demo

from datetime import date
from django.forms import widgets

class DateSelectorWidget(widgets.MultiWidget):
    def __init__(self, attrs=None):
        # create choices for days, months, years
        # example below, the rest snipped for brevity.
        years = [(year, year) for year in (2011, 2012, 2013)]
        _widgets = (
            widgets.Select(attrs=attrs, choices=days),
            widgets.Select(attrs=attrs, choices=months),
            widgets.Select(attrs=attrs, choices=years),
        )
        super().__init__(_widgets, attrs)

    def decompress(self, value):
        if value:
            return [value.day, value.month, value.year]
        return [None, None, None]

    def value_from_datadict(self, data, files, name):
        datelist = [
            widget.value_from_datadict(data, files, name + '_%s' % i)
            for i, widget in enumerate(self.widgets)]
        try:
            D = date(
                day=int(datelist[0]),
                month=int(datelist[1]),
                year=int(datelist[2]),
            )
        except ValueError:
            return ''
        else:
            return str(D)

先把这个demo跑起来吧

在app项目目录下,新建一个widgets.py,文件名自己起,自己觉得好就行。把上面的代码复制进去,你会发现缺少months、days数组。

在方法体中,有个decompress,就是处理这些数据的,具体怎么写,大家随便吧。我是直接弄了两个数组上去:

days = [1,2,3,4,5,6,7,8.9]
months = [1,2,3,4,5,6]

init()大家都知道是构造函数,用于初始化一些实例,变量等等。

后面的 “def value_from_datadict()"这个方法就是最后的返回数据。返回给谁呢?返回给调用它的对象啊。

在哪调用呢?
在models.py里面,先在app里建立一个forms.py文件。

#forms.py
from django import forms
from widgets import DateSelectorWidget

class testFeild(forms.CharField):
    widget = DateSelectorWidget

然后再models.py里调用:

from django.db import models
from .forms import testFeild
# Create your models here.
class test(models.Model):
    name = models.CharField(max_length=100)
    input = testFeild()
    def __str__(self):
        return self.name

这样,一个多选widget就被使用上了。但是,这不是本文的重点。

像省市区、多级多选如何实现?
网上90%是同一篇文章被爬虫、人为各种转载,恶心至极。
网上都是单选,相对简单些,因为单选的话,可以直接用Django自带的select widget,像上面例子里拿样,再构造函数里直接初始化现成的对象。如果Django没有的对象怎么办?

首先,自己写一个html模板,用于自定义用户输入界面,包括通过js获取输入的结果,保证js能拿到这个结果。

然后,在刚才新建的widgets.py文件里,新建一个类:

class SelectAreaFeild(widgets.Widget):
        template_name='home.html'
        def get_context(self, name, value, attrs=None):
            return {
                'widget':{
                    'name':name,
                    'value':value,
                }
            }

        #发送自定义模板
        def render(self, name, value, attrs=None, renderer=None):
            context = self.get_context(name,value,attrs)
            output = loader.render_to_string(self.template_name,
                                      {'widget_name': name, })
            return mark_safe(output)
        
        #格式化value
        def format_value(self, value):
            print(value)

        def value_from_datadict(self, data, files, name):
            return data.get(name, None)

把你的html文件放到app/templates目录下,用template_name='home.html' 这句引用。
def render() 这个方法的作用就是渲染模板,可以直接输出html语句。
get_context()获取上下文参数,这里把name,传递到html中,命名为:widget_name

在html中,你的自定义的表单,可以花里胡哨,但是要传值给Django,必须定义一个隐藏的input控件:

<input type="hidden" name={{ widget_name }}  v-model="mod_o">

v-model="mod_o" 这句的意思是赋值,把js获取到的值赋值给input,这样就可以使用了。

注意:
上述的方法,在models.py中是无法直接调用的,在models.py中定义成textFeild或者CharFeild,然后在admin.py
把字段指定widget。

from django.contrib import admin
from django.db import models as md
@admin.register(models.test)
class testAdmin(admin.ModelAdmin):
  list_display = ('name','input')
  formfield_overrides = {md.TextField:{'widget':SelectAreaFeild}}





相关文章

网友评论

      本文标题:Django自定义小部件(widget)

      本文链接:https://www.haomeiwen.com/subject/hbzwwftx.html