先引用一個官網的例子:
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對象。