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

相關文章
相關標籤/搜索