vmo*_*eco 5 django duplicates django-queryset
长话短说:
根据相关对象的值过滤查询集可能会导致结果中出现重复值。
limit_choices_to当以类似的方式使用模型字段时,此行为会在模型字段中的 FK 属性上传播,MultipleObjectsReturned从而在使用与此模型关联的模型表单并选择重复值时导致错误。
是否可以distinct()在模型的外键上应用或等效,limit_choices_to以避免模型表单字段的选项重复?
python manage.py shell(并解决它):设两个模型A和B:
class A(models.Model):
pass
class B(models.Model):
a = models.ForeignKey(A)
d = models.BooleanField(default=False)
Run Code Online (Sandbox Code Playgroud)
以及以下条目:
>>> a = A.objects.create()
>>> b1 = B.objects.create(a=a, d=True)
>>> b2 = B.objects.create(a=a, d=True)
Run Code Online (Sandbox Code Playgroud)
使用 aget()的以下查询集会导致错误:
>>> A.objects.filter(b__d=True).get(id=1)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/vmonteco/.venvs/django/lib/python3.6/site-packages/django/db/models/query.py", line 384, in get
(self.model._meta.object_name, num)
app.models.MultipleObjectsReturned: get() returned more than one A -- it returned 2!
Run Code Online (Sandbox Code Playgroud)
这听起来很正常,因为在结果a中出现了两次:filter()
>>> A.objects.filter(b__d=True)
<QuerySet [<A: A object>, <A: A object>]>
Run Code Online (Sandbox Code Playgroud)
这个错误可以通过简单的方法轻松解决distinct():
>>> A.objects.filter(b__d=True).distinct().get(id=1)
<A: A object>
Run Code Online (Sandbox Code Playgroud)
让我们添加第三个模型:
class C(models.Model):
a = models.ForeignKey(A, limit_choices_to={'b__d': True})
Run Code Online (Sandbox Code Playgroud)
我可以使用 modelform 创建/编辑实例:
class CForm(forms.ModelForm):
class Meta:
model = C
fields = ['a',]
Run Code Online (Sandbox Code Playgroud)
填充字段选择的查询集a应如下所示:
>>> A.objects.filter(b__d=True)
<QuerySet [<A: A object>, <A: A object>]>
Run Code Online (Sandbox Code Playgroud)
其中仅包含同一对象两次:
>>> A.objects.filter(b__d=True).values('id')
<QuerySet [{'id': 1}, {'id': 1}]>
Run Code Online (Sandbox Code Playgroud)
然后,在表单提交时,djangoget(id=selected_value)对字段的查询集应用 a 。如果所选值是重复值,则会出现我在上一部分中暴露的问题。
到目前为止,我找到的唯一解决方案是覆盖模型表单中字段的查询集,以确保没有重复:
class CForm(forms.ModelForm):
class Meta:
model = C
fields = ['a',]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['a'].queryset = self.fields['a'].queryset.distinct()
Run Code Online (Sandbox Code Playgroud)
但由于该查询集是在模型字段定义之后直接定义的,因此该解决方案感觉不令人满意,看起来更像是一种解决方法。limit_choices_to似乎没有记录这个案例。
limit_choices_to使用时是否有更合适的方法来避免字段查询集中出现重复?
我创建了一个可调用函数来获取选择列表,从而防止返回多行的外键查询:
class ParameterTypeGetter():
def __init__(self, class_name):
self.class_name = class_name
def __call__(self):
types = ParameterType.objects.filter(datatype__data_class__name=self.class_name)
return Q(parameter_type__in=types)
class ElementData(models.Model, VarDataCommentsMixIn):
parameter = models.ForeignKey(
Parameter, null=True, blank=True,
on_delete=models.PROTECT,
limit_choices_to=ParameterTypeGetter('parameter'))
multiparameter = models.ManyToManyField(
Parameter, related_name='multiparameter', blank=True,
limit_choices_to=ParameterTypeGetter('multiparameter'))
Run Code Online (Sandbox Code Playgroud)
我遇到这个问题时的情况是这样的:
limit_choices_to=dict(
parameter_type__datatype__data_class__name='parameter'),)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
452 次 |
| 最近记录: |