Django: как заставить зависимый выпадающий список загружать отфильтрованный список объектов на форме обновления?

У меня есть несколько зависимых выпадающих списков во всем моем приложении. Чтобы добавить новую запись, форма работает как ожидалось; например, вы выбираете Country и загружается список State объектов для этого Country, затем вы выбираете State и загружаются Suburb объекты для этого State. Вы сохраняете запись, в ней правильно сохранены все поля, просматриваете запись, и вся информация может быть просмотрена, как и ожидалось.

Проблема возникает, когда я хочу изменить/обновить запись. Инстанцированному SuburbForm приказано сначала загрузить пустой выпадающий список для State, а затем вызвать функцию load_dropdowns, когда родительский объект Country будет установлен. Это действие вызова load_dropdowns, похоже, не происходит в форме обновления, вместо этого она просто остается пустой, даже если я пытаюсь повторно выбрать родителя.

Если я изменю инстанцированную форму, чтобы она загружала все объекты вместо пустого выпадающего списка, форма обновления заполняет поле правильно, но это означает, что формы добавления нового и обновления теперь загружают все объекты вместо отфильтрованного списка.

models.py

from django.db import models

class Country(models.Model):
    item_name = models.CharField(verbose_name="Country")

class State(models.Model):
    item_name = models.CharField(verbose_name="State")
    state_in_country = models.ForeignKey(Country, on_delete=models.SET_NULL, verbose_name="Country", blank=True, null=True)

class Suburb(models.Model):
    item_name = models.CharField(verbose_name="Suburb")
    suburb_in_country = models.ForeignKey(Country, on_delete=models.SET_NULL, verbose_name="Country", blank=True, null=True)
    suburb_in_state = models.ForeignKey(State, on_delete=models.SET_NULL, verbose_name="State", blank=True, null=True)

forms.py

from django import forms
from .models import Country, State, Suburb

class CountryForm(forms.ModelForm):        
    class Meta:
        model = Country
        fields = [
            'item_name'
        ]

class StateForm(forms.ModelForm):
    class Meta:
        model = State
        fields = [
            'item_name', 
            'state_in_country'
        ]

class SuburbForm(forms.ModelForm):

    # separately define fields for dependent dropdowns
    suburb_in_country = forms.ModelChoiceField(queryset=Country.objects.filter(deleted=False),widget=forms.Select(attrs={"hx-get": "load_states_for_suburbform/", "hx-target": "#id_suburb_in_state"}),label="Country")

    # THE FOLLOWING VARIABLE RESULTS IN A BLANK DROPDOWN LIST WHICH CHANGES TO A FILTERED LIST ON ADD NEW FORM BUT REMAINS BLANK ON UPDATE FORM - SEE __init__ BELOW
    suburb_in_state = forms.ModelChoiceField(queryset=State.objects.none(),label="State")
        
    class Meta:
        model = Suburb
        fields = [
            'item_name', 
            'suburb_in_country',
            'suburb_in_state'
        ]

    def __init__(self, *args, **kwargs):
        super(SuburbForm, self).__init__(*args, **kwargs)

        # handle input from dependent dropdowns
        if 'suburb_in_country' in self.data:
            suburb_in_country = int(self.data.get("suburb_in_country"))
            self.fields['suburb_in_state'].queryset = State.objects.filter(state_in_country=suburb_in_country)     

views.py

from django.shortcuts import render,redirect,get_object_or_404
from .forms import *
from .models import *

...
def update(request, id, item):
    item = str_to_class(item)
    page_title = 'Update ' + item._meta.verbose_name
    instance = get_object_or_404(item, id=id)

    if request.method == 'POST':
        form = str_to_class(item._meta.verbose_name.strip() + 'Form')(request.POST, instance=instance)
        if form.is_valid():
            form.save()
        return redirect(item._meta.list_view)
    else:
        form = str_to_class(item._meta.verbose_name.strip() + 'Form')(instance=instance)
                     

    context = {'form':form, 
               'title': page_title,
               'list_view': item._meta.list_view}

    return render(request, 'create.html', context)
...
# Load data for dropdowns that depend on the choice of parent item
# requires model name (item) and the parent field to filter on (parent_field)
def load_dropdowns(request, item, parent_field):
    parent_id = 0 if request.GET.get(parent_field) in (None, '') else request.GET.get(parent_field) 
    if parent_id == 0:
        # parent item has not yet been chosen (or blank has been selected)
        return render(request, "emptydropdown.html")
    
    if item == State:
        # parent item of State is Country
        # show only states belonging to the chosen country
        items = State.objects.filter(state_in_country_id=parent_id, deleted=False).order_by('item_name')
    
    if item == Suburb:
        # parent item of Suburb is State
        # show only suburbs belonging to the chosen state
        items = Suburb.objects.filter(suburb_in_state_id=parent_id, deleted=False).order_by('item_name')
    return render(request, 'dependentdropdown.html', context={'items': items})
...
def load_states_for_suburbform(request):
    return load_dropdowns(request, State, 'suburb_in_country')

Решением является дополнительный оператор if в конце SuburbForm:

class SuburbForm(forms.ModelForm):

    # separately define fields for dependent dropdowns
    suburb_in_country = forms.ModelChoiceField(queryset=Country.objects.filter(deleted=False),widget=forms.Select(attrs={"hx-get": "load_states_for_suburbform/", "hx-target": "#id_suburb_in_state"}),label="Country")

    # THE FOLLOWING VARIABLE RESULTS IN A BLANK DROPDOWN LIST WHICH CHANGES TO A FILTERED LIST ON ADD NEW FORM BUT REMAINS BLANK ON UPDATE FORM - SEE __init__ BELOW
    suburb_in_state = forms.ModelChoiceField(queryset=State.objects.none(),label="State")
        
    class Meta:
        model = Suburb
        fields = [
            'item_name', 
            'suburb_in_country',
            'suburb_in_state'
        ]

    def __init__(self, *args, **kwargs):
        super(SuburbForm, self).__init__(*args, **kwargs)

        # handle input from dependent dropdowns
        if 'suburb_in_country' in self.data:
            suburb_in_country = int(self.data.get("suburb_in_country"))
            self.fields['suburb_in_state'].queryset = State.objects.filter(state_in_country=suburb_in_country)
        elif self.instance.pk:
            self.fields['suburb_in_state'].queryset = State.objects.filter(state_in_country=self.instance.suburb_in_country)
Вернуться на верх