下面是“Django admin实现动态多选框表单”的攻略。
背景介绍
Django是一个流行的Python Web框架,Django Admin是Django自带的管理后台。在Django Admin中,我们可以快速构建管理后台的界面和功能,并支持对数据库进行CURD操作。
动态多选框表单的需求
在Django Admin中,有时我们需要实现动态多选框表单,也就是当用户在一个fieldset中选择了某个选项时,当前fieldset之后的一些fieldset中的多选框选项需要动态更新。比如,我们需要根据选择的商品类别,动态更新商品品牌、商品型号等多个多选框的选项。
实现思路
我们可以通过JavaScript监听当前fieldset中某个选项的改变事件,然后异步请求服务端接口获取相关信息并更新后续多选框的选项。
在Django Admin中,我们可以使用Django自带的Inline Model Admin来实现这个功能,即在当前fieldset中嵌套另外一个Model Admin的Inline Model。然后在Inline Model中重载forms里的__init__方法,在页面加载的时候增加一个空白的表单用于后续异步更新。
示范例子
例子1:商品类别和商品品牌
下面是一个示例,假设我们的模型有两个ForeignKey字段,一个是商品类别,另一个是商品品牌,我们需要根据所选类别动态更新品牌选项。
# models.py
class Category(models.Model):
name = models.CharField(max_length=100)
class Brand(models.Model):
name = models.CharField(max_length=100)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
class Product(models.Model):
name = models.CharField(max_length=100)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
# admin.py
class BrandInline(admin.TabularInline):
model = Brand
extra = 0
def get_formset(self, request, obj=None, **kwargs):
formset = super().get_formset(request, obj, **kwargs)
form = formset.form
# 给form增加一个空白的brand表单
form.fields['name'].widget = TextInput(attrs={'class': 'vTextField', 'autocomplete': 'off', 'style': 'width:68%'})
form.fields['category'] = ModelChoiceField(queryset=Category.objects.all())
form.fields['category'].widget.attrs.update({'class': 'vForeignKeyRawIdAdminField', 'style': 'width: 255px'})
form.fields['category'].widget.to_field_name = 'id'
form.fields['category'].label = ''
form.fields['category'].empty_label = None
return formset
class ProductAdmin(admin.ModelAdmin):
inlines = [BrandInline]
class Media:
js = ('/static/js/admin/product.js',)
其中,product.js
文件如下:
// product.js
(function ($) {
$(document).ready(function () {
// 监听category多选框的change事件
$("#id_category").on("change", function () {
var categoryId = $(this).val();
// 异步加载brand选项
$.ajax({
url: "/admin/get-brands/",
type: "POST",
data: {
"csrfmiddlewaretoken": $("input[name=csrfmiddlewaretoken]").val(),
"category_id": categoryId
},
dataType: "json",
success: function (response) {
var options = "<option value=''>---------</option>";
// 构建brand多选框的选项
$.each(response.brands, function (i, brand) {
options += "<option value='" + brand.id + "'>" + brand.name + "</option>";
});
$("#brand_set-group select").html(options);
},
error: function (response) {
console.log("error: " + response);
}
});
});
});
})(django.jQuery);
在这个例子中,我们使用了Django内置的get_formset方法来修改Inline Model的表单。我们可以看到在get_formset中,我们把原来的brand表单fields中的category选项替换成ModelChoiceField,并去除了empty_label选项。这样可以在前端渲染时去掉Select中第一个空白选项。同时我们也会发现,这种方式下会出现很多的空白行,可以通过重载has_add_permission()方法来控制。
例子2:商品分类和商品型号
下面是另一个示例,假设我们的模型有两个ManyToMany字段,一个是商品分类,另一个是商品型号,我们需要根据所选分类动态更新型号选项。
# models.py
class Category(models.Model):
name = models.CharField(max_length=100)
class Model(models.Model):
name = models.CharField(max_length=100)
categories = models.ManyToManyField(Category)
class Product(models.Model):
name = models.CharField(max_length=100)
categories = models.ManyToManyField(Category)
models = models.ManyToManyField(Model)
# admin.py
class ModelInline(admin.TabularInline):
model = Product.models.through
extra = 0
def get_formset(self, request, obj=None, **kwargs):
formset = super().get_formset(request, obj, **kwargs)
form = formset.form
# 给form增加一个空白的model表单
form.fields['model'] = ModelMultipleChoiceField(queryset=Model.objects.none())
form.fields['model'].widget.attrs.update({'class': 'vManyToManyRawIdAdminField', 'style': 'width: 255px'})
form.fields['model'].label = ''
return formset
# 根据所选分类,异步加载对应的model选项
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name == "category":
def model_choices_for_category(category):
return Model.objects.filter(categories=category)
class ModelChoiceField(ModelChoiceField):
def __init__(self, category, *args, **kwargs):
super().__init__(*args, **kwargs)
self.category = category
self.choices = model_choices_for_category(category)
def queryset(self, request):
return model_choices_for_category(self.category)
ModelChoiceField.category = request.POST.get('category_set-0-category')
kwargs['queryset'] = Category.objects.none()
return ModelChoiceField(**kwargs)
return super().formfield_for_foreignkey(db_field, request=request, **kwargs)
class ProductAdmin(admin.ModelAdmin):
inlines = [ModelInline]
class Media:
js = ('/static/js/admin/product.js',)
在这个例子中,我们重载Inline Model的formfield_for_foreignkey方法,动态改变model的选项内容。在获取分类字段(db_field.name == "category")之后,我们使用输入分类ID后动态查询Model内容的方式来更新ModelMultipleChoiceField,当显示product页面需要修改对应行(ModelTable)右侧的ModelChoiceField多选框的时候,我们会发现Ajax动态更新的选项展示在了(product.js中 $('#model_set-group select[name$="-model"]').html(options);)最末尾中的特定div名的后面。
至此,两条Django admin实现动态多选框表单的完整示例代码攻略完毕。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:django admin实现动态多选框表单的示例代码 - Python技术站