tags:

views:

488

answers:

2
class StatusForm(ModelForm):

 bases = forms.ModelMultipleChoiceField(
   queryset=Base.objects.all(),  #this should get overwritten
   widget=forms.SelectMultiple,
  )

 class Meta:
  model = HiringStatus
  exclude = ('company', 'date')

 def __init__(self, *args, **kwargs):
  super(StatusForm, self).__init__(*args, **kwargs)
  if kwargs.has_key('bases_queryset'):
   self.fields['bases'].queryset = kwargs['bases_queryset']

I want to add an option to this form that allows me to create a form like so:

form = StatusForm(bases_queryset=Base.objects.filter([...])

But I somehow need to "add" that keyword argument to the class so it will be recognized. They way it is now, I just get this error:

__init__() got an unexpected keyword argument 'bases_queryset'

+7  A: 

That's because you're unpacking kwargs to the super constructor. Try to put this before calling super:

if kwargs.has_key('bases_queryset'):
    bases_queryset = kwargs['bases_queryset']
    del kwargs['bases_queryset']

but it's not an ideal solution...

Keeper
A better approach is simply:bases_queryset = kwargs.pop('bases_queryset', None)This removes the key from the dictionary and gives you its value, if it existed there, and also initializes to None when the key wasn't there.
James Bennett
yes that's better: I completely forgot about .pop method
Keeper
+8  A: 

As @Keeper indicates, you must not pass your "new" keyword arguments to the superclass. Best may be to do, before you call super's __init__:

bqs = kwargs.pop('bases_queryset', None)

and after that __init__ call, check if bqs is not None: instead of using has_key (and use bqs instead of kwargs['bases_queryset'], of course).

An interesting alternative approach is to make a "selective call" higher-order function. It's a bit of a mess if you have both positional and named arguments (that's always a bit messy to metaprogram around;-), so for simplicity of exposition assume the function you want to selectively call only has "normal" named arguments (i.e. no **k either). Now:

import inspect

def selective_call(func, **kwargs):
    names, _, _, _ = inspect.getargspec(func)
    usable_kwargs = dict((k,kwargs[k]) for k in names if k in kwargs)
    return func(**usable_kwargs)

with this approach, in your __init__, instead of calling super's __init__ directly, you'd call selective_call(super_init, **kwargs) and let this higher-order function do the needed "pruning". (Of course, you will need to make it messier to handle both positional and named args, ah well...!-)

Alex Martelli