如何用Django創建聯動選擇框
1 人贊了文章
在設計網頁的表單的時候,為了方便用戶的填寫和選擇,我們通常會在兩個選擇框進行聯動,通過第一個選擇框的選項去過濾第二個選擇框的選項。
比如第一個填寫國家和城市的時候,如果選國家的時候選中了某個具體的國家,那麼在選擇城市的選擇框中,就只會出現該國家的城市,其他國家的城市則不會出現在選項中。
Django中好像沒有直接的表單模式支持這樣的設計,所以這裡我整理了一下如何設計這種聯動選擇框。
廢話不多說,上代碼:
1. 首先創建國家和城市的model,注意將城市的foreign key設置為國家。
models.py
from django.db import models # Create your models here.class Country(models.Model): name = models.CharField(max_length=255) def __str__(self): return self.name class City(models.Model): country = models.ForeignKey(Country, on_delete=models.CASCADE) # 用外部key將城市和國家關聯起來 name = models.CharField(max_length=255) def __str__(self): return self.name
2. 通過控制台將model遷移進資料庫。
$ python manage.py makemigrations <project name>$ python manage.py migrate
3. 定義一個視圖函數,用這個視圖函數將根據國家篩選後的城市列表傳給一個名為drop_down_list_citiesd 的html頁面
views.py
from django.shortcuts import renderfrom .models import Country, City def home(request): form = CountryCityForm() if request.method == POST: form = CountryCityForm(request.POST) if form.is_valid(): form.save() return HttpResponseRedirect(reverse(demo:home)) context = {form: form} return render(request, "demo/home.html", context) def load_cities(request): country_id = request.GET.get(country) cities = City.objects.filter(country_id=country_id).order_by(name) context = {cities: cities} return render(request, demo/drop_down_list_cities.html, context)
4. 定義drop_down_list_cities的html模板,用來載入被選擇的國家裡的所有城市
drop_down_list_cities.html
{% if cities %} {% for c in cities %} <option value="{{ c.pk }}">{{ c.name }}</option> {% endfor %}{% endif %}
5. 定義url,使得我們能夠訪問localhost:port/ajax/load_cities這個鏈接,並且這個鏈接處理load_cities這個視圖函數。
urls.py
from django.urls import pathfrom . import views app_name = demo urlpatterns = [ path("", views.home, name="home"), path("ajax/load_cities", views.load_cities, name="load_cities"),]
6. 將選項欄放在Home頁面。
home.html
<!DOCTYPE html> <html> <title>Demo</html> <body> <form id="demo-form" action="." method="post" url="{% url demo:load_cities %}"> {% csrf_token %} {{ form.as_p }} <button name="submit">Submit</button> </form> </body></html>
7. 定義表單,我們用City作為表單的model,但是要去掉name欄位。不去掉的話,會出現一個可以輸入城市名的輸入框。
forms.py
from django import formsfrom .models import Country, City class CountryCityForm(forms.ModelForm): country = forms.ModelChoiceField(queryset=Country.objects.all(), required=True) city = forms.ModelChoiceField(queryset=City.objects.all(), empty_label=None, required=True) class Meta: model = City exclude = ("name",) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 繼承原有的__init__方法 self.fields[city].queryset = City.objects.none() # 將默認的city列表設置為空 if country in self.data: try: country = self.data.get(country) self.fields[city].queryset = City.objects.filter(country_id=country).order_by(name) except(ValueError, TypeError): pass elif self.instance.pk: self.fields[city].queryset = self.instance.country.city_set.order_by(name)
8. 將JQuery代碼放進home.html
home.html
<!DOCTYPE html> <html> <title>Demo</title> <h1>Home</h1> <body> <script src="https://cdn.staticfile.org/jquery/3.0.0/jquery.min.js"></script> <script> $(document).ready( function() { $("#id_country").change( function(event) { var country = $(this).val(); var url = $("#demo-form").attr("url"); $.ajax({ url: url, data: { country: country }, success: function (data){ $("#id_city").html(data); } }); }); }); </script> <form id="demo-form" action="." method="post" url="{% url demo:load_cities %}"> {% csrf_token %} {{ form.as_p }} <button name="submit">Submit</button> </form> </body></html>
中間的JQuery代碼的意思是去尋找id為id_country的選擇框,當它的值發生變化的時候,就向發送一個ajax請求。請求的url為我們在頁面form標籤里url屬性的值。請求的內容就是變化後的id_country選擇框當前選項的值。
如果請求成功的話,我們就用返回來的data去填充id_city選擇框。
這裡的id_country和id_city兩個標籤是Django的Form類的as_p方法自動生成的。
以上完成後,聯動選擇框就搞定啦~
推薦閱讀:
※python爬蟲無需登陸爬取知乎問題的首答
※用VS Code優雅地寫Python
※新版知乎登錄之post請求
※python基礎-try