django admin实现动态多选框表单的示例代码

下面是“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技术站

(0)
上一篇 2023年5月25日
下一篇 2023年5月25日

相关文章

  • 详解Django自定义图片和文件上传路径(upload_to)的2种方式

    Sure!下面是“详解Django自定义图片和文件上传路径(upload_to)的2种方式”的完整攻略。 方式1:在models.py中定义upload_to参数 在Django中,通常使用FileField或者ImageField来上传文件或者图片。这类字段包含一个upload_to参数,你可以指定这个参数来上传到自定义的路径。下面是示例代码: from …

    人工智能概览 2023年5月25日
    00
  • 树莓派4安装Ubuntu 19.10的教程详解

    树莓派4安装Ubuntu 19.10的教程详解 本文将详细介绍如何在树莓派4上安装Ubuntu 19.10,并提供两个示例说明。 准备工作 树莓派4B 2GB/4GB版 一张SD卡(建议使用16GB以上) 一台电脑(需要支持SD卡读写,并安装SD卡镜像刻录软件) 步骤一:下载Ubuntu 19.10 访问Ubuntu官网(https://ubuntu.com…

    人工智能概览 2023年5月25日
    00
  • python数据抓取分析的示例代码(python + mongodb)

    Python数据抓取分析是非常常见的一个应用场景,而Python与MongoDB的配合也非常流行。今天,我们将为大家介绍一份Python数据抓取分析的示例代码,使用Python和MongoDB进行数据的采集和存储,供大家参考借鉴。 1. 安装MongoDB 首先,需要安装并启动MongoDB数据库。安装可以参考MongoDB官方文档。 2. 安装Python…

    人工智能概论 2023年5月25日
    00
  • Linux中如何通过端口号查找进程号

    要在Linux中通过端口号查找进程号,可以使用以下方法: 步骤一:使用lsof命令查找进程 lsof(list open files)命令可以列出在系统中打开的文件和网络连接等信息。我们可以使用lsof命令找出使用某个端口号的进程。具体命令格式如下: lsof -i :端口号 其中“端口号”指的是需要查询的端口号。 例如,如果需要查找占用端口号为8080的进…

    人工智能概览 2023年5月25日
    00
  • SpringCloud-Config分布式配置代码示例

    下面是“SpringCloud-Config分布式配置代码示例”的完整攻略。 一、SpringCloud-Config分布式配置介绍 SpringCloud-Config是SpringCloud体系中的一个组件,它提供了分布式系统中的外部配置支持,可以将项目中的配置抽取到外部的配置服务器中管理。这样,当我们修改配置时,不需要重新部署应用,只需要把新的配置信息…

    人工智能概览 2023年5月25日
    00
  • tensorflow 自定义损失函数示例代码

    下面是关于”tensorflow 自定义损失函数示例代码”的完整攻略: 1. 自定义损失函数的介绍 在深度学习中,损失函数是评估模型效果的重要指标之一,它可以用来衡量模型预测结果与真实值之间的差异。在tensorflow中,我们可以使用内置的损失函数,例如MSE、交叉熵等,同时也可以根据自己的需求自定义损失函数。 自定义损失函数可以通过tensorflow框…

    人工智能概论 2023年5月25日
    00
  • python计算寄送包裹重量的实现过程

    当计算寄送包裹重量时,Python可以用以下的代码实现: 实现过程 步骤一:定义变量 定义变量用于存储不同物品的重量和数量,以及总重量和单位。 weight_items = [2.5, 1.8, 3.2, 4.5] # 邮包物品的重量 quantity_items = [3, 2, 1, 4] # 邮包物品的数量 total_weight = sum([w*…

    人工智能概论 2023年5月25日
    00
  • Keepalived+Nginx+Tomcat 实现高可用Web集群的示例代码

    Keepalived+Nginx+Tomcat 实现高可用Web集群的示例代码 简介 本文将介绍如何通过Keepalived+Nginx+Tomcat实现高可用Web集群,并提供示例代码。 准备工作 3台服务器,主服务器A和两个备份服务器B和C,可任意选择物理机或虚拟机。 安装CentOS 7.x操作系统。 在每台服务器上安装Nginx和Tomcat,并且确…

    人工智能概览 2023年5月25日
    00
合作推广
合作推广
分享本页
返回顶部