0

我有一个模型Jar,它有一个crate属性——ForeignKey一个Crate模型。该Crate模型有一个capacity属性(它可以容纳的罐子的数量)和一个jars属性(它当前容纳的罐子的数量),它是这一行: return self.jar_set.filter(is_active=True).count().

我有一个管理操作,可以将多个罐子移动到一个新的板条箱中。它使用中间页面来选择目标板条箱。现在所有板条箱都列在下拉列表中,但我想将列出的板条箱限制为只有那些有空间容纳所选罐子数量的板条箱。如何?

这是来自的管理操作admin.py

class MoveMultipleJarsForm(forms.Form):
    # This needs to somehow be restricted to those crates that have room
    dest = forms.ModelChoiceField(queryset=Crate.objects.all().order_by('number'))

def move_multiple_jars(self, request, queryset):
    form = None

    if 'apply' in request.POST:
        form = self.MoveMultipleJarsForm(request.POST)

        if form.is_valid():
            dest = form.cleaned_data['dest']

            count = 0
            for jar in queryset:
                jar.crate = dest
                jar.save()
                count += 1

            plural = ''
            if count != 1:
                plural = 's'

            self.message_user(request, "Successfully moved %d jar%s to %s" % (count, plural, dest))
            return HttpResponseRedirect(request.get_full_path())
    if not form:
        form = self.MoveMultipleJarsForm()

    return render(request, 'admin/move_multiple_jars.djhtml', {
        'jars': queryset,
        'move_multiple_jars_form': form,
        })

move_multiple_jars.short_description = "Move multiple jars to new crate"
4

1 回答 1

1

在 Python Developers Slack 服务器上的 layibug 的帮助下,我想出了一个解决方案。

class MoveMultipleJarsForm(forms.Form):
    dest = forms.ModelChoiceField(Crate.objects.none())

    def __init__(self, *args, **kwargs):
        count = kwargs.pop('count')
        super().__init__(*args, **kwargs)
        self.fields['dest'].queryset = Crate.objects.annotate(room=F('capacity')-Sum(Case(When(jar__is_active=True, then=1), default=0), output_field=IntegerField())).filter(room__gte=count).order_by('number')


def move_multiple_jars(self, request, queryset):
    form = None

    if 'apply' in request.POST:
        form = self.MoveMultipleJarsForm(request.POST)

        if form.is_valid():
            dest = form.cleaned_data['dest']

            count = 0
            for jar in queryset:
                jar.crate = dest
                jar.save()
                count += 1

            plural = ''
            if count != 1:
                plural = 's'

            self.message_user(request, "Successfully moved %d jar%s to %s" % (count, plural, dest))
            return HttpResponseRedirect(request.get_full_path())
    if not form:
        form = self.MoveMultipleJarsForm(count=queryset.count())

    return render(request, 'admin/move_multiple_jars.djhtml', {
        'jars': queryset,
        'move_multiple_jars_form': form,
        })

move_multiple_jars.short_description = "Move multiple jars to new crate"

解决方案的第一步是修改表单类以将查询集中 jar 的数量作为初始化值传递,然后可以更改目标 crate 列表。我有一个 90% 的解决方案,SubqueryOuterRef它无法处理没有活动罐子的板条箱。我在这个问题中接受了答案:How to filter objects for count annotation in Django? 奇怪的是,我 90% 的解决方案与他们针对 Django 1.11 的解决方案相似,但我猜他们并不担心没有参与者的情况。我在那个问题中给出了另一个被接受的答案,因为如果我最初看到它,我可能不必发布这个问题。

于 2017-09-25T22:32:56.380 回答