Django modelformset is_valid 方法从数据库中检索整个模型

Django modelformset is_valid method retrieves entire model from database

当调用 is_valid 方法时,我有一个 modelformset 从数据库中检索底层表单的整个模型对象,如果数据库 table 包含数十万条记录,那么效率不高,我相信 Django 的设计没有这个缺陷,我的代码一定有问题,当调用 is_valid 时,这些查询被发送到数据库,第一个查询没有 WHERE 子句,这意味着它将检索整个 table!:

[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'},
{'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'},

**{'sql': 'SELECT `npr`.`rid`, `npr`.`PID`, `npr`.`rel`, `npr`.`CID` FROM `npr` ORDER BY `npr`.`rid` ASC', 'time': '0.037'}**

{'sql': 'SELECT `person_details`.`ID`, `person_details`.`firstName` FROM `person_details` WHERE `person_details`.`ID` = 198 LIMIT 21', 'time': '0.001'},
{'sql': 'SELECT `person_details`.`ID`, `person_details`.`firstName` FROM `person_details` WHERE `person_details`.`ID` = 1243 LIMIT 21', 'time': '0.000'},
{'sql': 'SELECT `npr`.`rid`, `npr`.`PID`, `npr`.`rel`, `npr`.`CID` FROM `npr` WHERE `npr`.`rid` = 1377 LIMIT 21', 'time': '0.000'},
{'sql': 'SELECT (1) AS `a` FROM `person_details` WHERE `person_details`.`ID` = 198 LIMIT 1', 'time': '0.000'},
{'sql': 'SELECT (1) AS `a` FROM `person_details` WHERE `person_details`.`ID` = 1243 LIMIT 1', 'time': '0.000'},
{'sql': 'SELECT (1) AS `a` FROM `npr` WHERE (`npr`.`CID` = 1243 AND `npr`.`PID` = 198 AND NOT (`npr`.`rid` = 1377)) LIMIT 1', 'time': '0.000'},
{'sql': 'SELECT `person_details`.`ID`, `person_details`.`firstName` FROM `person_details` WHERE `person_details`.`ID` = 200 LIMIT 21', 'time': '0.000'},
{'sql': 'SELECT `person_details`.`ID`, `person_details`.`firstName` FROM `person_details` WHERE `person_details`.`ID` = 1243 LIMIT 21', 'time': '0.004'},
{'sql': 'SELECT `npr`.`rid`, `npr`.`PID`, `npr`.`rel`, `npr`.`CID` FROM `npr` WHERE `npr`.`rid` = 1378 LIMIT 21', 'time': '0.000'},
{'sql': 'SELECT (1) AS `a` FROM `person_details` WHERE `person_details`.`ID` = 200 LIMIT 1', 'time': '0.000'},
{'sql': 'SELECT (1) AS `a` FROM `person_details` WHERE `person_details`.`ID` = 1243 LIMIT 1', 'time': '0.000'},
{'sql': 'SELECT (1) AS `a` FROM `npr` WHERE (`npr`.`CID` = 1243 AND `npr`.`PID` = 200 AND NOT (`npr`.`rid` = 1378)) LIMIT 1', 'time': '0.000'}]

模特

   class PersonDetails(models.Model):
        id = models.AutoField(db_column='ID', primary_key=True)
        firstname = models.CharField(db_column='firstName', max_length=20)
    
        class Meta:
            managed = True
            db_table = 'person_details'
    
    
    class Npr(models.Model):
        rid = models.AutoField(db_column='rid', primary_key=True)
        pid = models.ForeignKey(PersonDetails, on_delete=models.CASCADE, db_column='PID', related_name='pid')
        rel = models.CharField(max_length=1)
        cid = models.ForeignKey(PersonDetails, on_delete=models.CASCADE, db_column='CID', related_name='cid')
    
        class Meta:
            managed = True
            db_table = 'npr'
            unique_together = (('pid', 'cid'),)

和表单定义:

class prelation(ModelForm):
    class Meta:
        model = Npr
        fields = ['pid', 'rel', 'cid']
        widgets = {'pid':HiddenInput(), 'rel':HiddenInput(), 'cid':HiddenInput()}

prelations = modelformset_factory(Npr, form = prelation, can_delete=True, extra=1)

当针对模型集调用 is_valid 方法时,如何优化发送到数据库的查询?我正在使用 Django 版本:3.0.4,Python 版本:3.7.3,数据库:10.3.22-MariaDB-0+deb10u1-log

编辑 1: 我正在使用 Django shell 进行测试,因为我想让问题简洁并避免整个应用程序的复杂性,并且两者都给出了相同的结果。

>>> from tstapp.forms import prelations
>>> 
>>> 
>>> datana={'form-TOTAL_FORMS':'2',
... 'form-INITIAL_FORMS':'2',
... 'form-MIN_NUM_FORMS':'0',
... 'form-MAX_NUM_FORMS':'1000',
... 'form-0-pid':'198',
... 'form-0-rel':'F',
... 'form-0-cid':'1243',
... 'form-0-rid':'1377',
... 'form-0-DELETE':'None',
... 'form-1-pid':'200',
... 'form-1-rel':'M',
... 'form-1-cid':'1243',
... 'form-1-rid':'1378',
... 'form-1-DELETE':'None'}
>>> 
>>> form=prelations(data=datana)
>>> connection.queries
[]
>>> form.is_valid()
True
>>> connection.queries
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'}, {'sql': 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED', 'time': '0.000'}, {'sql': 'SELECT `npr`.`rid`, `npr`.`PID`, `npr`.`rel`, `npr`.`CID` FROM `npr` ORDER BY `npr`.`rid` ASC', 'time': '0.037'}, {'sql': '

还好;我找到了缺失的部分,在文档中它说 (Changing the queryset):

By default, when you create a formset from a model, the formset will use a queryset that includes all objects in the model (e.g., Author.objects.all()). You can override this behavior by using the queryset argument:

我没有在验证表单集之前更改查询集,这导致 Django 检索整个基础模型并将所有对象包含在模型中。在创建将要验证的表单时,我应该使用与生成表单集相同的查询集。验证应如下所示:

qs = prs.objects.filter(Q(cid = 1243) | Q(pid = 1243))
form=prelations(data=datana, queryset = qs)