2011-08-15 32 views
1

我想明白爲什麼明確指定簽名參數不起作用,但只是盲目地做*參數,** kwargs作品!我真的沒有看到兩者之間有太大的區別?簽名中的顯式參數不起作用?很奇怪

示例不起作用:

from django.db.models import CharField as _CharField 

class CharField(_CharField): 
    def get_db_prep_value(self, value, connection, prepared=False): 
     if self.blank == self.null == self.unique == True and value == '': 
      value = None 

     return super(CharField, self).get_db_prep_value(value, connection, prepared) # <--- this does not work! 

,我得到以下錯誤:

File "/home/googledroid/Workspace/eclipse/gameproject/virtualenv/lib/python2.6/site-packages/django/db/models/fields/__init__.py", line 276, in get_db_prep_save 
    return self.get_db_prep_value(value, connection=connection, prepared=False) 
    File "/home/googledroid/Workspace/eclipse/gameproject/virtualenv/lib/python2.6/site-packages/django/db/models/fields/subclassing.py", line 53, in inner 
    return func(*args, **kwargs) 
    File "/home/googledroid/Workspace/eclipse/gameproject/src/fields/__init__.py", line 13, in get_db_prep_value 
    return super(CharField, self).get_db_prep_value(value, connection, prepared) 
    File "/home/googledroid/Workspace/eclipse/gameproject/virtualenv/lib/python2.6/site-packages/django/db/models/fields/subclassing.py", line 53, in inner 
    return func(*args, **kwargs) 
    File "/home/googledroid/Workspace/eclipse/gameproject/virtualenv/lib/python2.6/site-packages/django/db/models/fields/subclassing.py", line 53, in inner 
    return func(*args, **kwargs) 
TypeError: get_db_prep_value() got multiple values for keyword argument 'connection' 

雖然這工作得很好:

from django.db.models import CharField as _CharField 

class CharField(_CharField): 
    def get_db_prep_value(self, value, *args, **kwargs): 
     if self.blank == self.null == self.unique == True and value == '': 
      value = None 

     return super(CharField, self).get_db_prep_value(value, *args, **kwargs) 

在Django源,django.db.models.subclassing.call_with_connection_and_prepared.inner(),我看到有一些kwargs的刪除,但不完全確定爲什麼?

回答

3

事情是,connection參數應該始終作爲關鍵字參數傳入。 django.db.models.fields.subclassing中的代碼僅檢查它是否存在於kwargs字典中,如果不存在,則發出DeprecationWarning並將其添加到那裏。位置參數沒有被檢查,所以最終發生的是你傳遞的位置參數被轉發,但是函數包裝器默認提供的關鍵字參數也被傳入。因此衝突。

爲了使您的代碼的工作,所有你需要做的是這樣的:

 return super(CharField, self).get_db_prep_value(value, connection=connection, prepared=prepared) 

僅供參考,在開發版本中的所有那些包裝已被刪除,這意味着當前的代碼將針對主幹可能工作。但是,最好保留參數kwargs

+0

這很有道理。謝謝!爲了確保我是100%的,子類在調用原始的Field.get_db_prep_value()方法之前做到了這一點,對嗎?這就是爲什麼Field.get_db_prep_value()方法不受影響(儘管位於繼承鏈的頂部)。 – GoogleDroid

+0

事情是,在Django 1.3中。X,所有的字段類都使用元類「LegacyConnection」。這個元類用它的方法'db_type','get_db_prep_save','get_db_prep_lookup'和'get_db_prep_value'替換它們的包裝,它們執行以下操作:1)檢查包裝方法是否接受'connection'作爲參數; 2)已經提供了連接** kwarg **。在內部,ORM中的所有Django機制都使用kwargs,並且在進行子類化時,您也應該使用它們。 – koniiiik

+0

我對django的內部結構並不十分清楚,所以沒有100%清楚,並且很可能得到了它的要點,但謝謝! :D – GoogleDroid

0

我沒有Django的源可用,所以這只是一個猜測:

class Foo(object): 
    def get_db_prep_value(self,*args,**kwargs): 
     print(args,kwargs) 

class Bar(Foo): 
    def get_db_prep_value(self,value,connection,prepared=False): 
     super(Bar,self).get_db_prep_value(value,connection,prepared) 

class Baz(Foo): 
    def get_db_prep_value(self,*args,**kwargs):   
     super(Baz,self).get_db_prep_value(*args,**kwargs) 

bar=Bar() 
bar.get_db_prep_value(1,2,prepared=True) 
# ((1, 2, True), {}) 

baz=Baz() 
baz.get_db_prep_value(1,2,prepared=True) 
# ((1, 2), {'prepared': True}) 

注意什麼獲取傳遞給Foo取決於BarBaz呼叫簽名是不同的

當您使用

super(Bar,self).get_db_prep_value(value,connection,prepared) 

prepared被傳遞到位置ARG列表列表args

但是當你使用

super(Baz,self).get_db_prep_value(*args,**kwargs) 

prepared被傳遞到關鍵字字典kwargs

+0

感謝您指出這一點。 – GoogleDroid

0

對於使用「double splat」語法定義的函數,我調用它時,調用者必須明確地爲每個關鍵字參數提供字典密鑰。我敢打賭,get_db_prep_value被定義爲下面的第一個函數。

def usessplat(**kwargs): 
    print 'connection = ' + kwargs.get('connection', 'None') 

def nosplat(connection=None) 
    print 'connection = ' + str(connection) 

usessplat('foo') # raises TypeError 
nosplat('foo') # no exception