Reputation: 18107
Base class looks like this (code comes from Django, but the question isn't Django specific):
class BaseModelForm(BaseForm):
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList, label_suffix=':',
empty_permitted=False, instance=None):
class ModelForm(BaseModelForm):
__metaclass__ = ModelFormMetaclass
Derived class looks like this:
class MyForm(forms.ModelForm):
def __init__(self, data=None, files=None, *args, **kwargs):
super(EdocForm, self).__init__(data, files, *args, **kwargs)
self.Meta.model.get_others(data, files, kwargs.get('instance', None))
If the coder passes instance as a kwargs this is all fine and should work, but what if they don't do that and pass it in args instead? What is a sane way to extract instance when dealing with *args and **kwargs? Admittedly in this case the chances of instance being in args is small, but if it was the 3rd argument instead of the 5th (not counting self).
Upvotes: 1
Views: 722
Reputation: 17106
If you insist on accepting *args
, one way to handle this situation if getting instance
matters to MyForm
, is to explicitly include instance
as a keyword argument to MyForm
, add instance to kwargs, and then pass up kwargs.
class MyForm(forms.ModelForm):
def __init__(self, data=None, files=None, instance=None, *args, **kwargs):
kwargs['instance'] = instance
super(EdocForm, self).__init__(data, files, *args, **kwargs)
Note that, if someone put instance as the third positional argument, they would be making a very explicit error, since instance is the last argument to BaseModelForm.
However, a better way to handle this situation would be to specifically not allow additional positional arguments. E.g.:
class MyForm(forms.ModelForm):
def __init__(self, data=None, files=None, **kwargs):
super(EdocForm, self).__init__(data, files, *args, **kwargs)
self.Meta.model.get_others(data, files, kwargs.get('instance', None))
That way, MyForm
can only be called with up to 2 positional arguments. Any more and the Python interpreter will generate a TypeError: <func> takes exactly 2 arguments (# given)
.
If you need to use the instance
argument, you should explicitly include it in the keyword arguments to MyForm
. Failing that, you at least should note it in the doc string to the function.
If you're subclassing BaseModelForm and it has a self.instance
variable, you could access it directly via super. E.g. self.instance = super(EdocForm, self).instance
. That way, you let the superclass handle whatever it needs to do with instance and grab the instance for yourself post-processing. (be careful about syntax with super...you need both the class and self)
Upvotes: 0
Reputation: 11130
Well in django if instance is not passed or its not properly set then it becomes None and init will then create a new instance for you http://docs.nullpobug.com/django/trunk/django.forms.models-pysrc.html#BaseModelForm But this isn't your question. If the other variables aren't properly set an exception will probably be raised.
Its really up to you if you want to check if the user has properly used your API, and in python this can be quite daunting, and checking for instance types isn't very pythonic, there are very heated debate though out the web about whether its a good or bad thing to have a language thats so dynamically typed. On one hand you can be very productive in a dynamically typed language on the other you can have really nasty bugs whose solution aren't that apparent, but thats from my own experience.
I believe consistency is one of the most crucial things a system could have, as such I tend to always use keyword values, all set to None and then simply do an assert to make sure all that are required aren't None, you could have a function that just checks your parameters are good. Keyword arguments tend to be the most flexible, from my own experience, since you don't need to keep track of the order, you pay the price by having the callee remember its name.
If you really need 'instance' then you can iterate over args and/or kwargs using isinstance to get the instance variable, though like I said this isn't very pythonic....
Upvotes: 1