先引用一个官网的例子:
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对象。