简体   繁体   中英

Why does Django models OneToOne query all objects for drop-down when ForeignKey does not?

在此处输入图像描述 models.py

class PumpLog(models.Model):
    # work_order = models.OneToOneField('WorkOrder', on_delete = models.CASCADE)
    work_order = models.ForeignKey('WorkOrder', on_delete = models.CASCADE)

class WorkOrder(models.Model):
    completed_on = models.DateField('Completed On', null=True, blank=True)

template_.html

{{ form.work_order|as_crispy_field }}

forms.py

class TempForm(forms.ModelForm):
    '''Initializes a mostly blank form with initial values for fields specified in default_values_list'''            
    initial = {}
    page_type = self.page_type
    if page_type == 'detail':
        data = self.request.POST or None
    for default_value in default_values_list:
        if default_value in self.kwargs:
            initial[default_value] = self.kwargs[default_value]
    def clean(self):
        ...
    def __init__(self, *args, **kwargs):
        kwargs['initial'] = self.initial
        super(TempForm, self).__init__(*args, **kwargs)
        
form = modelform_factory(model, TempForm,exclude = exclude_list,widgets = widgets)

As you can see there is a FK relationship between PumpLog and WorkOrder . When the PumpLog-Update page loads it only queries the selected/related WorkOrder . If the drop-down to select the work order is clicked - it queries additional WorkOrder as the user scrolls or searches.

I am trying to convert it to OneToOne . But when I switch it to OneToOne I notice that it retrieves all WorkOrders - which can take 1-4 minutes to load. I noticed this by placing print(self.index ) in the WorkOrder __str__ method. In the console I see it list every single WorkOrder one-by-one. But if I switch it back to FK it only displays the index of the one already selected in the drop-down.

This doesn't seem to be a widget or crispy_form issue because I see the same behaviour when I remove or revert those.

I understand to some degree that it needs access to all of the work orders so that the user can select whichever one. BUT it doesn't seem to need to load all WorkOrders when the relationship is FK. Why is it behaving differently when I use OneToOne ?

@markwalker_ pointed out

...django loads all related objects unless you override it.

This guided me to discovering that I had in fact overrode FK drop-downs by default via widgets. This would also explain why I experienced the same sluggishness when commenting out the widgets while using the OneToOne.

In fact if I had commented out the widgets while using the FK relationship I would have seen a similar sluggishness.

The way I override it is first check for any FK fields (which didn't check for OneToOne fields)

def get_foreign_keys(model):
    foreign_keys = []
    for field in model._meta.fields:
        if field.get_internal_type() == 'ForeignKey':
            foreign_keys.append(field) 
    return foreign_keys
...
foreign_keys = get_foreign_keys(model)

I then use the autocomplete ModelSelect2 widget which handles the lazy loading which I took for granted:

for fk in foreign_keys:
    widgets[field_name] = autocomplete.ModelSelect2(url = url, forward=forward)

My quick-and-dirty solution is to check for OneToOne fields in get_foreign_keys():

def get_foreign_keys(model):
    foreign_keys = []
    for field in model._meta.fields:
        if field.get_internal_type() == 'ForeignKey':
            foreign_keys.append(field) 
        elif field.get_internal_type() == 'OneToOneField':
            foreign_keys.append(field) 
        print(field.get_internal_type())
    return foreign_keys   

A more long term solution will simply be refactoring some naming to accommodate both FK and O2O for that function. And also some additional documentation - so I can better navigate old code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM