Динамический выпадающий на промежуточной странице для действий администратора

У меня есть модель 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"

1 ответ

Решение

С помощью layibug на сервере Python Developers Slack я смог найти решение.

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-файлов в наборе запросов в качестве значения инициализации, которое могло бы затем изменить список ящиков назначения. У меня было 90% решение с Subquery а также OuterRef но он не мог обрабатывать ящики без активных банок. Я пошел с принятым ответом в этом вопросе: Как отфильтровать объекты для подсчета комментариев в Django? Как ни странно, мое 90% -ое решение похоже на их решение для Django 1.11, но я думаю, что они не беспокоились по поводу случая отсутствия участников. Я дал принятый ответ на этот вопрос еще раз, потому что, если бы я видел его первоначально, мне, возможно, не пришлось бы публиковать этот вопрос.

Другие вопросы по тегам