compressiondjango-multiwidget

django multiwidget subclass not calling decompress()


I am trying to implement a MultiValueField for IP Adress/Domain Name entries. It works as expected for entering data. My Problem is that if I want to display the form bound to specific data, the IP Address/Domain Name field stays empty. All other fields are filled with the desired data. If I use a normal CharField, I get the data that I would expect. But it does not work with my custom field. I have tracked it down to the fact that my custom MultiWidget does not call its decompress method.

Here is my Field:

class accessIPField(forms.MultiValueField):
    """                                                                         
    custom Field for access IP                                                  
    """                                                                         

    def __init__(self, *args, **kwargs):                                        
        self.fields=(                                                           
            forms.IPAddressField(label='IP Adress'),                            
            forms.CharField(max_length=50,label='Domain Name')                  
        )                                                                       
        self.widget=accessIPWidget()                                            
        super(accessIPField,self).__init__(self.fields,self.widget, *args, **kwargs)

    def compress(self,data_list):                                               
        if data_list:                                                           
            return " ".join(data_list) 

And here is my widget:

class accessIPWidget(forms.MultiWidget):
    """ 
    Widget to display IP Adress / Domain name pairs
    """
    def __init__(self,*args,**kwargs):
        self.widgets=(forms.TextInput(),forms.TextInput())
        super(accessIPWidget,self).__init__(self.widgets,*args,**kwargs)

    def decompress(self,value):
        print 'decompress called'
        if value:
            return value.rsplit()
        return [None,None]

    def format_output(self, rendered_widgets):
        return u'\n'.join(rendered_widgets)

The whole thing is called (in a larger context) as

self.fields['access_IPs'] = accessIPField()

Now as you can see, I put a print statement in my compress method, and I never get to see that statement. Also, if I rename compress to something like foobar, I would expect (according to the django code for MultiWidget) to get the NotImplementedError, which is not the case. Any suggestions?

I am using python 2.6.5, django 1.1 on ubuntu server 10.04.


Solution

  • It turns out that the problem was with the value_from_datadict() method as implemented by MultiWidget. First of all, it allready returned a list, so that is why decompress() was not called in the first place. Secondly, it allways returen a [None,None] list, so that is why the bound form stayed empty.

    I needed to implement my own (within my accessIPWidget class):

    def value_from_datadict(self, data, files, name):
    try:
      return data.get(name,None).rsplit()
    except AttributeError:
      return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)]
    

    Now the last line is what the original method did. In order to get the data into the bound form, I needed to add data.get(name,None).rsplit().

    As far as I understand, the original value_from_datadict method only works for unbound fields. Because it changes the name of the original field to name + '_%s', which is what you get when pressing the submit button. In order to fill in a bound method, the datadict needs to be queried for 'name' only.

    Hm, not shure if there is a way around this, but it seems to me that this behaviour should at least be documented somewhere. Maybe I misunderstood something?