django.forms-Widget和Media间的联系

先引用一个官网的例子:
css

from django import formsclass 

CalendarWidget(forms.TextInput):
    class Media:
        css = {
            'all': ('pretty.css',)
        }
        js = ('animations.js', 'actions.js')

这是经过内置Media类来实现js和css引用的。python

下面讲解,它实例化的顺序django

首先看Widget类的定义:
函数

class Widget(six.with_metaclass(MediaDefiningClass)):
    ......

   with_metaclass()方法返回一个经过元类动态生成的类。
测试

而后看元类MediaDefiningClass的定义:this

class MediaDefiningClass(type):
    """
    Metaclass for classes that can have media definitions.
    """
    def __new__(mcs, name, bases, attrs):
        new_class = (super(MediaDefiningClass, mcs)
            .__new__(mcs, name, bases, attrs))

        if 'media' not in attrs:
            new_class.media = media_property(new_class)

        return new_class

经过复写__new__方法,来动态添加media属性。spa

它首先会判断Widget类有没有media属性, 没有就经过media_property()方法添加。code


接着来探究media_property()方法是如何添加media属性的:orm

def media_property(cls):
    def _media(self):
        # Get the media property of the superclass, if it exists
        sup_cls = super(cls, self)
        try:
            base = sup_cls.media
        except AttributeError:
            base = Media()

        # Get the media definition for this class
        definition = getattr(cls, 'Media', None)
        if definition:
            extend = getattr(definition, 'extend', True)
            if extend:
                if extend is True:
                    m = base
                else:
                    m = Media()
                    for medium in extend:
                        m = m + base[medium]
                return m + Media(definition)
            else:
                return Media(definition)
        else:
            return base
    return property(_media)

由于涉及到继承的缘由, 因此会有些复杂。media的继承是经过制定extend属性。对象

media默认会自动继承父类, 也能够设置extend = False取消继承。固然也能够指定extend = ( 'js' )或着( 'css' ,  'js' ),

选择性的继承某部分。


首先它会获取父类的media,不然返回空的media。而后根据extend的值,来添加继承的部分。

而后结合内置Media类的属性,返回最后的media。

最后返回通过property()包装的_media函数。

property()是常常用来包装对属性的访问。能够看出,咱们经过Widget.media访问media属性,获取到的是动态生成的。

这也意味着,每次返回的结果都是同一个media。能够经过id( )函数测试。


以上都是经过内置Media类来达到对js和css的引用。还有种方法是直接定义media。

仍旧引用官网的例子:

class CalendarWidget(forms.TextInput):
    def _media(self):
        return forms.Media(css={'all': ('pretty.css',)},
                           js=('animations.js', 'actions.js'))
    media = property(_media)

这样一样能够达到相同的效果, 可是不能继承。

还有个优势是,咱们能够制定返回的是新的media对象, 也能够是同一个media对象。

相关文章
相关标签/搜索