jazzband / django-floppyforms

Full control of form rendering in the templates.
http://django-floppyforms.readthedocs.org/
Other
837 stars 148 forks source link

GeoDjango widgets in admin site cannot be used as regular widgets with the Meta widgets attribute #213

Closed javiermatos closed 4 years ago

javiermatos commented 4 years ago

Hi,

I use to set custom widgets in my ModelForm for the admin site using the following pattern:

class PointWidget(floppyforms.gis.PointWidget, floppyforms.gis.BaseOsmWidget):
    pass

class AccountForm(forms.ModelForm):
    address_location = floppyforms.gis.PointField(widget=PointWidget())  # THIS WORKS!

    class Meta:
        model = models.Account
        fields = '__all__'
        widgets = {
            'description': AdminTextareaWidget(),
            'services': FilteredSelectMultiple(verbose_name='services', is_stacked=False),
            # 'address_location': PointWidget(),  # THIS DOES NOT WORK!
        }

But I cannot do the same thing with a PointWidget. It works when setting address_location (my model field) directly as an attribute, but it does not work when using the widgets dictionary. There in my code sample there are two comments to show the different widget usages.

Is this behaving as expected? Am I forced to just use the approach that works instead of any other valid approach for standard Django widgets?

Regards!

rtpg commented 4 years ago

Hi @javiermatos , sorry for the late reply on this (still figuring out the new Github notifications....)

So basically this is working "as expected". The way that Django works for this stuff when building a model form is:

  1. generate a set of fields for a model (using fields_for_model). So here this would be a call like

      opts = cls.Meta
      default_fields = fields_for_models(models.Account, opts.fields, ..., opts.widgets, ....)

    2 - set those up on the class (so at this point default_fields will use the right widget, but it would not use the floppyforms.gis.PointField but the default Django one)

    3 - override these defaults with manually declared fields. for example you declared address_location here, so that field overrides everything from fields_for_model (including stuff built up on meta)


Now, if you want to use a custom widget, you have a couple options (haven't tested any of these but I believe variants of these ideas work)

class CustomFormPointField(loppyforms.gis.PointField):
    widget = CustomPointWidget

class CustomModelPointField(PointField):  # this is a model field
    field_class = CustomFormPointField

# use the above in your models, then form fields will automatically use the right kind of field
class PointWidget(floppyforms.gis.PointWidget, floppyforms.gis.BaseOsmWidget):
    pass

class AccountForm(forms.ModelForm):

    class Meta:
        model = models.Account
        fields = '__all__'
        widgets = {
            'description': AdminTextareaWidget(),
            'services': FilteredSelectMultiple(verbose_name='services', is_stacked=False),
            'address_location': PointWidget(),  # widget declared here...
        }
        field_classes = {
           'address_location': floppyforms.gis.PointField  # custom form field declared here
       }
class PointWidget(floppyforms.gis.PointWidget, floppyforms.gis.BaseOsmWidget):
    pass

from django.db.contrib.gis.db.models.fields import PointField as DjangoPointField

DjangoPointField.widget = PointWidget

Hope this helps

javiermatos commented 4 years ago

Great! Thank you so much for the detailed explanation! I had it working using the recommended way so it is fine. Thank you! :)

rtpg commented 4 years ago

Glad to hear that you were able to get things working 😄