Vicious cycle: Cannot migrate a DB that uses a widget using a model

6 views
Skip to first unread message

Victor Porton

unread,
Jun 19, 2019, 9:40:23 AM6/19/19
to Django users
When I try to migrate my project from empty DB state, it falls into a vicious cycle: Trying to migrate it requests Country.objects.all() because it has form fields using PlaceWidget but this requires it to be already migrated.

How to solve this problem?

class PlaceWidget(widgets.MultiWidget):
template_name = 'core/widgets/placewidget.html'

class Media:
js = ('js/placewidget.js',)

def __init__(self, attrs=None):
_widgets = (
widgets.Select(choices=[('', '-')] + [(c.pk, c.name) for c in Country.objects.all().order_by('name')],
attrs={'onchange': "update_places_list(1)", **(attrs or {})}),
widgets.Select(choices=[('', '-')],
attrs={'onchange': "update_places_list(2)", **(attrs or {})}),
widgets.Select(choices=[('', '-')],
attrs={'onchange': "update_places_list(3)", **(attrs or {})}),
widgets.Select(choices=[('', '-')],
attrs={'onchange': "update_places_list(4)", **(attrs or {})}),
widgets.Select(choices=[('', '-')])
)
super().__init__(_widgets, attrs)

def decompress(self, value):
if value:
return [value['country'].pk if value['country'] else None,
value['region'].pk if value['region'] else None,
value['subregion'].pk if value['subregion'] else None,
value['city'].pk if value['city'] else None,
value['district'].pk if value['district'] else None]
return [None, None, None, None, None]

Simon Charette

unread,
Jun 19, 2019, 9:58:13 AM6/19/19
to Django users
Hello Victor,

You should avoid performing database queries at module loading level as the code will
necessary crash if the database is missing or unmigrated.

I suggest you lazily define widgets instead by using a cached property[0]

class PlaceWidget(widgets.MultiWidget):
    def __init__(self, attrs=None):
        # Bypass MultiWidget.__init__ to allow lazy definition of widgets.
        super(widgets.MultiWidget, self).__init__(attrs)

    @cached_property
    def widgets(self):
        return (
            ... # the _widgets you defined in your __init__.
        )

This should make sure the Country.objects.order_by('name') query is only executed
when .widgets is accessed for the first time on a PlaceWidget instance instead of at
initialization time.

Cheers,
Simon

Reply all
Reply to author
Forward
0 new messages