Filter a Django form select element based on a previously selected element

前端 未结 2 1728
栀梦
栀梦 2021-02-03 11:28

Let\'s consider the following models

models.py

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


class CarModel(models.Model         


        
相关标签:
2条回答
  • 2021-02-03 11:55
    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)
    

    Here is my Form

    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)
    
    0 讨论(0)
  • 2021-02-03 12:06

    After hours and hours of research, without success, I decided to try to solve on my own. The solution that I found maybe don't be the best or the more elegant, but is working. (For download full Django project, click on this 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
    

    The goal is to register cars on the fleet. The only fields that are will be recorded: Car (foreign key) and description. On the form, there will be one select element for brands that will work just only as a helper for to filter the car's combo box.


    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 is a form for register cars, there are three fields: brand_select, car_select, and description. In addition, I defined two JSON attributes: 1) a dictionary whose keys are brands (strings) and values are lists of respective's cars and 2) A list of strings that represent the brands. Those two attributes will work as helpers for JS functions.


    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})
    

    The view is practically auto-explanatory. Assigns the Form to the car_form variable, render the template core/regcar.html and, after Post, make the validation of the form and save the data.


    regcar.html (template 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 %}
    

    The template only just renders the form and load the script JS. Nothing else.


    Finally, the js script, that makes the hard work.

    {% 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 %}
    

    When the document is loaded, this script assigns the change event of brand_select (combo for selection of brand) to the function poplulateCar, assign the form's JASON attributes (cars and brands) to a JS variables and call the populateBrand function.

    Links:

    Full project in Django:
    https://github.com/Sidon/djfkf/

    0 讨论(0)
提交回复
热议问题