【问题标题】:Filter a Django form select element based on a previously selected element根据先前选择的元素过滤 Django 表单选择元素
【发布时间】:2017-08-06 20:08:41
【问题描述】:

让我们考虑以下模型

models.py

Class Brand(models.Model):
    company_name = models.CharField(max_length=100)


class CarModel(models.Model):
    brand = models.ForeignKey(Brand)
    name = models.CharField(max_length=100)


Class FleetCars(models.Model):
    model_car = models.Foreignkey(CarModel)

在 django 中解决此问题的最佳方法是什么? 假设一个表单(用于FleetCars 中的插入)由两个选择元素组成,如下所示:

<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>

<br />Brand:
<select>
   <option value="Brand1">Brand1</option>
   <option value="Brand2">Brand2</option>
</select>
<br />
<br />Model:
<select>
   <option value="Model1_B1">Model1_B1</option>
   <option value="Model1_B2">Model1_B2</option>
</select>

</body>
</html>

在这种情况下,我希望第二次选择中的选项取决于第一次选择的值。例如,如果用户在第一个选择中为 Brand 选择 Brand1,则第二个选择将仅过滤 Brand 为 Brand1 的汽车,即仅“Model1_B1”。

观察。 我看到了很多forms.ModelChoiceField 的解决方案,但仅适用于编辑,因为用户不会更改品牌。

【问题讨论】:

  • 有一个名为 smart_selects 的应用程序提供 ChainedForeignKey 字段。

标签: django


【解决方案1】:

经过数小时的研究,没有成功,我决定尝试自己解决。我发现的解决方案可能不是最好的或更优雅的,但正在工作。 (要下载完整的 Django 项目,请单击此 repo => https://github.com/Sidon/djfkf/。)


models.py

from django.db import models

class Brand(models.Model):
    company_name = models.CharField(max_length=100)

    def __str__(self):
        return self.company_name


class Car(models.Model):
    brand = models.ForeignKey(Brand)
    name = models.CharField(max_length=100)

    def brand_name(self):
        return self.brand.company_name

    def __str__(self):
        return self.name


class Fleet(models.Model):
    car = models.ForeignKey(Car)
    description = models.CharField(max_length=100)

    def car_name(self):
        return self.car.name

    def brand(self):
        return self.car.brand.company_name

    def __str__(self):
        return self.description

目标是在车队中注册汽车。将记录的唯一字段:汽车(外键)和描述。在表单上,​​将有一个品牌选择元素,仅作为过滤汽车组合框的助手。


forms.py

import json
from django import forms
from .models import *

class RegCarForm(forms.ModelForm):

    dcars = {}
    list_cars = []
    for car in Car.objects.all():
        if car.brand.company_name in dcars:
            dcars[car.brand.company_name].append(car.name)
        else:
            dcars[car.brand.company_name] = [car.name]
        list_cars.append((car.name,car.name))

    brands = [str(brand) for brand in Brand.objects.all()]

    brand_select = forms.ChoiceField(choices=([(brand, brand) for brand in brands]))
    car_select = forms.ChoiceField(choices=(list_cars))

    brands = json.dumps(brands)
    cars = json.dumps(dcars)

    class Meta:
        model = Fleet
        fields = ('brand_select', 'car_select', 'description',)

RegCarForm 是注册汽车的表单,共有三个字段:brand_select、car_select 和description。此外,我定义了两个 JSON 属性:1)一个字典,其键是品牌(字符串),值是各自汽车的列表;2)代表品牌的字符串列表。这两个属性将作为 JS 函数的助手。


views.py

from django.shortcuts import render
from .forms import RegCarForm
from .models import *

def regcar(request):
    if request.method == 'POST':
        car_form = RegCarForm(data=request.POST)

        if car_form.is_valid():
            cdata = car_form.cleaned_data.get
            car_selected = Car.objects.filter(name=cdata('car_select'))
            reg1 = Fleet(car_id=car_selected[0].id, description=cdata('description'))
            reg1.save()
        else:
            print ('Invalid')

    else:
        car_form = RegCarForm()
    return render(request, 'core/regcar.html', {'car_form': car_form})

该视图实际上是不言自明的。将 Form 分配给 car_form 变量,渲染模板 core/regcar.html,并在 Post 之后对表单进行验证并保存数据。


regcar.html(模板 django)

{% extends "base.html" %}

{% block head %}
{% endblock %}

{% block content %}
    <h1>Registering cars on the fleet. <br />(Populate one drop down based on selection in another)</h1>
    <p>Change the contents of drop down Car based on the selection in dropdown Brand, using Django-forms + Javascritp</p>
    <div class="select-style">
        <form action="." method="post">
            {% csrf_token %}
            {{ car_form.as_p }}
            <p><input type="submit" value="Register a car"></p>
        </form>
    </div>
{% endblock %}

{% block js %}
    {% include "js1.html" %}
{% endblock %}

模板只是渲染表单和加载脚本JS。没有别的了。


最后是js脚本,辛苦了。

{% block js %}
    <script language="javascript">
        $('#id_brand_select').change(function() {populateCar(this)});
        $('#id_description').addClass('descriptions');
        cars = {{ car_form.cars | safe }}
        brands = {{ car_form.brands | safe}};
        populateBrand();
        $("#id_car_select").empty();
        $("#id_car_select").append('<option value="" disabled selected>First select a brand</option>');
        function populateBrand() {
            $('#id_brand_select').empty();
            $("#id_brand_select").append('<option value="" disabled selected>Select your option</option>');
            $.each(brands, function(v) {
                $('#id_brand_select')
                    .append($("<option></option>")
                    .attr("value", brands[v])
                    .text(brands[v]));
            });
        }

        function populateCar(event) {
            brand = $("#id_brand_select option:selected").text();
            $("#id_car_select").empty();
            $("#id_car_select").append('<option value="" disabled selected>Select your option</option>');
            for (let [b, bcars] of Object.entries(cars)) {
                if (b == brand) {
                    //alert(b);
                    for (car in bcars) {
                        $('#id_car_select')
                            .append($("<option></option>")
                                .attr("value", bcars[car])
                                .text(bcars[car]));
                    }
                }
            }
        }
    </script>
{% endblock %}

加载文档时,此脚本将brand_select(用于选择品牌的组合)的更改事件分配给函数poplulateCar,将表单的JASON属性(汽车和品牌)分配给JS变量并调用populateBrand函数。

链接:

Django 中的完整项目:
https://github.com/Sidon/djfkf/

【讨论】:

  • 不使用表格是否可以做到这一点?
  • 可以,但是您只能使用表单来创建 jsons。
  • 如果不需要表单,在视图中创建json。
  • 我需要像你一样的动态选择框,但不使用表单,我使用它来获取方法。您能否详细说明一下没有表单的选择框或其他一些方法来实现这一点
  • 只需将创建 json 的代码从 forms.py 移动到 view.py。
【解决方案2】:
class Country(models.Model):
   country_name=models.CharField(max_length=10, blank=True, null=True)

class State(models.Model):
   state_name=models.CharField(max_length=10, blank=True, null=True)

class MyCustomModal(models.Model):
   country = models.ForeignKey(Country, on_delete=models.CASCADE, null=True, blank=True)
   state = models.ForeignKey(State, on_delete=models.CASCADE, null=True, blank=True)

这是我的表格

class MyCustomForm(forms.ModelForm):
    class Meta:
        model = MyCustomModal
        fields = [
            'country',
            'state',           
        ]        

    def __init__(self, *args, **kwargs):
        super(MyCustomForm, self).__init__(*args, **kwargs)        
        self.fields['country'] = forms.ChoiceField(choices=[('1','india'),('2','US')])
        self.fields['state'].queryset = State.objects.filter(pk=2)

【讨论】:

    猜你喜欢
    • 2012-10-23
    • 2017-07-22
    • 1970-01-01
    • 2018-06-24
    • 1970-01-01
    • 1970-01-01
    • 2013-01-25
    • 1970-01-01
    • 2012-03-18
    相关资源
    最近更新 更多