简体   繁体   中英

Django/jQuery Cascading Select Boxes in Admin

I have a Contract class where contract_mod allows to extend a contract from a previous one. contract_mod should only show contracts related with the person that we selected previously. The Contract class returns the field person . As I've never work with AJAX/jQuery I don't know where to start.

So, my goal is that the field contract_mod depends on hte field person (using the Admin interface).

class Contract(models.Model):
    person        = models.ForeignKey(Person) #person hired
    contract_mod  = models.OneToOneField('self', blank = True, null = True) #allows to extend a contract
        ...

As the field contract_mod is OneToOneField I can't use django-smart-selects or django-ajax-select

In a similar situation I did the following (now adapted to your model):

models

class Person(models.Model):
    name = models.CharField(max_length=20)
    def __unicode__(self):
        return self.name
    def get_name(self):
        return self.name

class Contract(models.Model):
    person        = models.ForeignKey(Person) #person hired
    contract_mod  = models.OneToOneField('self', blank = True, null = True)
    contract_name = models.CharField(max_length=20) #just for testing

    def __unicode__(self):
        return self.get_name() + " " +self.contract_name
    def get_name(self):
        return self.person.get_name() #to make sure you get the person name in the admin
    def contract_mod_name(self):
        if self.contract_mod:
            return self.contract_mod.contract_name
        else:
            return ""

admin

class SelectField(forms.ChoiceField):
    def clean(self, value):
        return value

class CForm(forms.ModelForm):
    contracts_from_selected = SelectField()
    class Meta:
        model = Contract
        widgets = { 'contract_mod' : forms.widgets.Select(attrs={'hidden' : 'true'}) }

class CAdmin(admin.ModelAdmin):
    form = CForm
    list_display = ('contract_name','get_name','contract_mod_name')#what you like
    def save_model(self, request, obj, form, change):
        if request.POST.get('contracts_from_selected'):
            obj.contract_mod=Contract.objects.get(id=int(request.POST.get('contracts_from_selected')))

        obj.save()

Override the change_form.html template (by copying it from the django/contrib/admin/templates/admin into your yourapp/templates/admin/yourapp directory) and add the following Javascript:

$(function () {
    $("#id_person").change(function () {    
        var options = $("#id_contract_mod option").filter(function () {
            return $(this).html().split(" ")[0] === $("#id_person   option:selected").html();
        }).clone();
        $("#id_contracts_from_selected").empty();
        $("#id_contracts_from_selected").append(options);
    });
});

One shortcoming is, that this uses the visible html entry to store the person - contract relation. So the person is also visible in the dropdown. To avoid this you could add an attribute to the options instead - see here: Django form field choices, adding an attribute

Yes and it would be good to completely hide the contract_mod ChoiceField. Via the hidden=True in the widget only the dropdown is hidden.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM