Django模型字段动态Choice的四种实现方案与最佳实践

木白
木白
发布时间:2026-10-13| 4人浏览过
原创

本文深入探讨Django中根据当前登录用户动态调整模型字段Choice的多种实现路径。通过重写Form的__init__方法、定制Admin的changeform_view及render_change_form,以及结合前端Ajax请求与JS逻辑,详细对比了四种方案的优劣。文章指出直接暴露用户列表至前端的隐患,并推荐后端接口配合Ajax的动态获取方式,同时补充了优雅使用Choice字段的代码规范建议。

Django动态Choice需求概述

核心需求在于根据当前登录用户的身份,动态展示模型中某个Choice字段的不同可选值。

首先介绍一种基础的实现版本。

Form层动态处理方案

第一种方案:重写PushRuleForm的__init__方法,

确保每次实例化PushRuleForm时,test_mode字段的choices依据用户身份重新赋值。


class PushRuleForm(forms.ModelForm):  
  def __init__(self, *args, **kwargs):
    if self.request.user.username in Const.TEST_USER_LIST:
      # 如果进入都是add添加新项的页面
      if not kwargs.get('instance'):
        # self.fields['test_mode'].initial = 1
        self.fields['test_mode'].choices = [(1,'Test')]
      # else:
      #   self.fields['test_mode'].choices = [choice for choice in [(0,'OnLine'),(1,'Test')] if self.instance.test_mode in choice]

第二种方案:重写PushRuleAdmin的changeform_view方法。由于进入add(新增)和change(编辑)页面时都会调用该方法,重写此方法可使form获取request属性,从而通过request.user.username获取用户名。


class PushRuleAdmin:
  form = PushRuleForm
  def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
    self.form.request = request
    return super(PushRuleAdmin, self).changeform_view(request, object_id, extra_context=extra_context)

mode.py对应的代码如下:


class PushRule(models.Model):  
  test_mode  = models.IntegerField(verbose_name='TestMode', default=0, choices=[(0,'OnLine'),(1,'Test')])

Admin层上下文传递与JS处理

实现方式2:

重写PushRuleAdmin的render_change_form方法,传入test_user_list上下文,通过JS判断当前用户是否属于测试用户。


class PushRuleAdmin:
  def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
    context['test_user_list']=Const.TEST_USER_LIST
    return super(PushRuleAdmin, self).render_change_form(request, context, add=False, change=False, form_url='', obj=None)

JS代码:


  if (test_user_list.includes(username))
  {
    $("#id_test_mode > option[value='0']").remove();
  }

HTML模板代码:

为了让JS获取Django模板变量,先定义一个username和test_user_list变量

<script> var username="{{ user.username }}", test_user_list="{{ test_user_list }}"</script>

然而这种做法的弊端在于用户列表信息直接暴露在前端代码中,效果等同于在JS里硬编码维护测试用户列表,因此最终放弃此方案。

Ajax接口交互方案

实现方式3:

在后端编写一个视图接口,返回对应的test_user_list,JS中通过Ajax请求该视图以获取数据。

经实测,该方案运行正常。

其他尝试与未实现思路

实现方式4:

类似方法2,但不通过JS处理,而是直接重写django/contrib/admin/templates/admin/includes/fieldset.html模板文件,利用Django模板语法处理。由于对Django模板语法不够熟悉,此方案也被放弃。

未实现的思路:尝试在PushRuleAdmin中直接修改model的test_mode字段的choice选项,但未成功。

试图修改model的fields,发现其为ImmutableList类型,直接修改会报错。

不过StackOverflow上提供了一种参考方法,值得借鉴,主要缺憾在于缺少获取用户名的环节,后续可进一步研究。

Django Choice字段最佳实践

补充知识:Django中优雅地使用Choice字段

问题背景:

在Django中如何优雅地对元组进行标记分类?可以使用Choice字段。

Choice字段


# models.py
class BookTagNum(object):
  OTHER = 1
  SCIENCE = 2
  SOCIAL_SCIENCES = 3
  ECONOMIC = 4
  COMPUTER = 5

class BOOK(models.Model):
  TAG_NUM_CHOICE = (
    (BookTagNum.OTHER, '其它'),
    (BookTagNum.SCIENCE, '科学类'),
    (BookTagNum.SOCIAL_SCIENCES, '社科类'),
    (BookTagNum.ECONOMIC, '经济类'),
    (BookTagNum.COMPUTER, '计算机类'),
  )
  tag = models.IntegerField(choices=TAG_NUM_CHOICE)

在代码中应尽量避免出现固定的硬编码,例如某个判断条件,判断书的分类为:


# view.py
def get(self, request):
 book = Book.obejects.filter(tag = BookTagNum.COMPUTER)

以上这篇Django实现模型字段动态Choice的操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本站。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。