Explicit argument in signature does not work? Very odd
I'm trying to understand why explicitly specifying the signature arguments doesn't work, but just blindly doing an *args, **kwargs works! I really don't see much difference between the two?
Example that does not work:
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!
and I get the following error:
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'
While this works just fine:
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)
In django source, django.db.models.subclassing.call_with_connection_and_prepared.inner()
,I see there is some deletion of kwargs, but not entirely sure why?
The thing is, the connection
argument is supposed to be always passed in as a keyword argument. The code in django.db.models.fields.subclassing
only checks whether it is present in the kwargs
dictionary, if not, it issues a DeprecationWarning
and adds it in there. The positional arguments are not checked, so what happens in the end is that both the positional argument you passed gets forwarded, but the keyword argument provided by default by the function wrapper gets passed in as well. Hence the conflict.
To make your code work, all you need to do is this:
return super(CharField, self).get_db_prep_value(value, connection=connection, prepared=prepared)
Just FYI, in the development version all those wrappers have been removed, which means your current code would probably work against trunk. However, it is considered best to keep the arguments in kwargs
.
I don't have the django source available, so this is just a guess:
Notice that what gets passed to Foo
is different depending on the call signature in Bar
and Baz
:
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})
When you use
super(Bar,self).get_db_prep_value(value,connection,prepared)
prepared
gets passed into the positional argument list args
.
But when you use
super(Baz,self).get_db_prep_value(*args,**kwargs)
prepared
gets passed into the keyword dict kwargs
.
For functions defined using the "double splat" syntax, as I call it, the caller must explicitly give the dictionary key for each keyword arguments. I would bet that the get_db_prep_value
is defined like the first of the below functions.
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
精彩评论