如何在Django中用ModelChoiceFields实现二级下拉动态联动
Alright, let's tackle this dynamic category-subcategory dropdown requirement in Django. This is a pretty common use case, and we can make it work smoothly with a mix of backend setup and frontend AJAX calls—no need to submit the entire form to update the subcategories.
1. Update Your Form Definition
First, we need to initialize the subcategory field with an empty queryset since we’ll populate it dynamically. I’m assuming your SubCategory model already has a foreign key relationship to Category (like category = models.ForeignKey(Category, on_delete=models.CASCADE)—if not, set that up first!).
class AddProductForm(forms.Form): category = forms.ModelChoiceField( widget=forms.Select(attrs={'class': 'form-control'}), queryset=Category.objects.all(), to_field_name="category" ) sub_category = forms.ModelChoiceField( widget=forms.Select(attrs={'class': 'form-control'}), queryset=SubCategory.objects.none() # Start with empty options )
2. Create a Backend View to Fetch Subcategories
We’ll build a simple view that takes a category ID and returns matching subcategories as JSON. This will be the endpoint our frontend calls when the user selects a different category.
from django.http import JsonResponse from .models import SubCategory def get_subcategories(request): category_id = request.GET.get('category_id') # Adjust 'sub_category' below to match your actual display field name in SubCategory subcategories = SubCategory.objects.filter(category_id=category_id).values('id', 'sub_category') return JsonResponse(list(subcategories), safe=False)
3. Map the View to a URL
Add this path to your app’s urls.py so the frontend can access the endpoint:
from django.urls import path from . import views urlpatterns = [ # ... your existing URLs path('get-subcategories/', views.get_subcategories, name='get_subcategories'), ]
4. Add Frontend AJAX Logic
Now we need to listen for changes on the category dropdown, fetch the corresponding subcategories, and update the subcategory dropdown in real-time. I’ll show both jQuery and vanilla JS options—pick whichever fits your project.
Using jQuery (Simpler for Most Projects)
Make sure you include jQuery in your template first, then add this script:
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script> <script> $(document).ready(function() { const $category = $('#id_category'); const $subCategory = $('#id_sub_category'); // Triggered when category selection changes $category.change(function() { const categoryId = $(this).val(); $.ajax({ url: "{% url 'get_subcategories' %}", data: { 'category_id': categoryId }, dataType: 'json', success: function(data) { // Clear old options and add a placeholder $subCategory.empty(); $subCategory.append('<option value="">-- Select Sub Category --</option>'); // Populate new subcategory options $.each(data, function(index, subcat) { $subCategory.append(`<option value="${subcat.id}">${subcat.sub_category}</option>`); }); } }); }); // Optional: Auto-load subcategories if a category is pre-selected if ($category.val()) { $category.trigger('change'); } }); </script>
Using Vanilla JavaScript (No jQuery Needed)
If you prefer to avoid jQuery, use this script instead:
<script> document.addEventListener('DOMContentLoaded', function() { const categoryDropdown = document.getElementById('id_category'); const subcategoryDropdown = document.getElementById('id_sub_category'); categoryDropdown.addEventListener('change', function() { const categoryId = this.value; const url = `{% url 'get_subcategories' %}?category_id=${categoryId}`; fetch(url) .then(response => response.json()) .then(data => { // Clear existing options subcategoryDropdown.innerHTML = ''; // Add placeholder option const placeholder = document.createElement('option'); placeholder.value = ''; placeholder.textContent = '-- Select Sub Category --'; subcategoryDropdown.appendChild(placeholder); // Add subcategory options data.forEach(subcat => { const option = document.createElement('option'); option.value = subcat.id; option.textContent = subcat.sub_category; subcategoryDropdown.appendChild(option); }); }); }); // Auto-load if category is pre-selected if (categoryDropdown.value) { categoryDropdown.dispatchEvent(new Event('change')); } }); </script>
Quick Notes to Avoid Issues
- Double-check your
SubCategorymodel field names: replacesub_categoryin the view and frontend code with the actual field that stores the subcategory’s display name. - Since we’re using a GET request here, we don’t need to handle Django’s CSRF token. If you switch to POST later, you’ll need to include the CSRF token in the request headers.
- Adjust the
requiredstatus of thesub_categoryform field based on your needs (addrequired=Trueif users must select a subcategory).
内容的提问来源于stack exchange,提问作者AMIT SINGH




