django.forms-Widget和Media间的联系

先引用一个官网的例子:

from django import formsclass 

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

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

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

首先看Widget类的定义:

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

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

然后看元类MediaDefiningClass的定义:

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属性。

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


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

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对象。


你可能感兴趣的:(django.forms-Widget和Media间的联系)