Django 表单模型选择框如何使用分组


Posted in Python onMay 16, 2019

起步

Django 表单中有两种字段类型可以使用选择框: ChoiceFieldModelChoiceField

对于 ChoiceField 的基本使用是:

class ExpenseForm(forms.Form):
  CHOICES = (
    (11, 'Credit Card'),
    (12, 'Student Loans'),
    (13, 'Taxes'),
    (21, 'Books'),
    (22, 'Games'),
    (31, 'Groceries'),
    (32, 'Restaurants'),
  )
  date = forms.DateField()
  category = forms.ChoiceField(choices=CHOICES)

它能渲染出:

Django 表单模型选择框如何使用分组

使用分组下拉框

还可以使用如下方式生成 <optgourp> 标签:

class ExpenseForm(forms.Form):
  CHOICES = (
    ('Debt', (
      (11, 'Credit Card'),
      (12, 'Student Loans'),
      (13, 'Taxes'),
    )),
    ('Entertainment', (
      (21, 'Books'),
      (22, 'Games'),
    )),
    ('Everyday', (
      (31, 'Groceries'),
      (32, 'Restaurants'),
    )),
  )
  date = forms.DateField()
  category = forms.ChoiceField(choices=CHOICES)

能够渲染为:

Django 表单模型选择框如何使用分组

分组模型下拉框

如果使用的是 ModelChoiceField ,那抱歉,Django本身没有提供解决方案。

在 https://code.djangoproject.com/ticket/27331 中提供了一个很好的解决方案。

首先为需要分类的类型创建模型,在另一个模型中用外键关联它:

from django.db import models

class Category(models.Model):
  name = models.CharField(max_length=30)
  parent = models.ForeignKey('Category', on_delete=models.CASCADE, null=True)

  def __str__(self):
    return self.name

class Expense(models.Model):
  amount = models.DecimalField(max_digits=10, decimal_places=2)
  date = models.DateField()
  category = models.ForeignKey(Category, on_delete=models.CASCADE)

  def __str__(self):
    return self.amount

其次,创建一个新的表单 Field 类型:

from functools import partial
from itertools import groupby
from operator import attrgetter

from django.forms.models import ModelChoiceIterator, ModelChoiceField

class GroupedModelChoiceIterator(ModelChoiceIterator):
  def __init__(self, field, groupby):
    self.groupby = groupby
    super().__init__(field)

  def __iter__(self):
    if self.field.empty_label is not None:
      yield ("", self.field.empty_label)
    queryset = self.queryset
    # Can't use iterator() when queryset uses prefetch_related()
    if not queryset._prefetch_related_lookups:
      queryset = queryset.iterator()
    for group, objs in groupby(queryset, self.groupby):
      yield (group, [self.choice(obj) for obj in objs])

class GroupedModelChoiceField(ModelChoiceField):
  def __init__(self, *args, choices_groupby, **kwargs):
    if isinstance(choices_groupby, str):
      choices_groupby = attrgetter(choices_groupby)
    elif not callable(choices_groupby):
      raise TypeError('choices_groupby must either be a str or a callable accepting a single argument')
    self.iterator = partial(GroupedModelChoiceIterator, groupby=choices_groupby)
    super().__init__(*args, **kwargs)

最后,在表单中可以如下进行使用:

from django import forms
from .fields import GroupedModelChoiceField
from .models import Category, Expense

class ExpenseForm(forms.ModelForm):
  category = GroupedModelChoiceField(
    queryset=Category.objects.exclude(parent=None), 
    choices_groupby='parent'
  )

  class Meta:
    model = Expense
    fields = ('amount', 'date', 'category')

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
在Python中操作字符串之replace()方法的使用
May 19 Python
python下paramiko模块实现ssh连接登录Linux服务器
Jun 03 Python
python简单读取大文件的方法
Jul 01 Python
详解基于django实现的webssh简单例子
Jul 17 Python
python实现汉诺塔算法
Mar 01 Python
Python参数解析模块sys、getopt、argparse使用与对比分析
Apr 02 Python
Opencv实现抠图背景图替换功能
May 21 Python
python安装pil库方法及代码
Jun 25 Python
pandas 层次化索引的实现方法
Jul 06 Python
使用APScheduler3.0.1 实现定时任务的方法
Jul 22 Python
详解python datetime模块
Aug 17 Python
python 实现两个变量值进行交换的n种操作
Jun 02 Python
详解pandas如何去掉、过滤数据集中的某些值或者某些行?
May 15 #Python
详解Python列表赋值复制深拷贝及5种浅拷贝
May 15 #Python
Python 20行简单实现有道在线翻译的详解
May 15 #Python
Python中的字符串切片(截取字符串)的详解
May 15 #Python
python3 property装饰器实现原理与用法示例
May 15 #Python
详解Python下载图片并保存本地的两种方式
May 15 #Python
Python常用模块之requests模块用法分析
May 15 #Python
You might like
php 显示指定路径下的图片
2009/10/29 PHP
php过滤表单提交的html等危险代码
2014/11/03 PHP
PHP微信刮刮卡 附微信接口
2016/07/22 PHP
php读取出一个文件夹及其子文件夹下所有文件的方法示例
2017/06/15 PHP
php学习笔记之mb_strstr的基本使用
2018/02/03 PHP
JavaScript函数、方法、对象代码
2008/10/29 Javascript
Jquery ajax传递复杂参数给WebService的实现代码
2011/08/08 Javascript
Js base64 加密解密介绍
2013/10/11 Javascript
javascript 回到顶部效果的实现代码
2014/02/17 Javascript
javascript操作字符串的原生方法
2014/12/22 Javascript
针对BootStrap中tabs控件的美化和完善(推荐)
2016/07/06 Javascript
jquery 属性选择器(匹配具有指定属性的元素)
2016/09/06 Javascript
详解vue-cli与webpack结合如何处理静态资源
2017/09/19 Javascript
在Vue项目中使用jsencrypt.js对数据进行加密传输的方法
2019/04/17 Javascript
bootstrap-treeview实现多级树形菜单 后台JSON格式如何组织?
2019/07/26 Javascript
vue和iview实现Scroll 数据无限滚动功能
2019/10/31 Javascript
vue 路由守卫(导航守卫)及其具体使用
2020/02/25 Javascript
vue下canvas裁剪图片实例讲解
2020/04/16 Javascript
element-ui 弹窗组件封装的步骤
2021/01/22 Javascript
浅谈Python2、Python3相对路径、绝对路径导入方法
2018/06/22 Python
Python面向对象之接口、抽象类与多态详解
2018/08/27 Python
使用Django连接Mysql数据库步骤
2019/01/15 Python
Python对象与引用的介绍
2019/01/24 Python
python实现一个简单的ping工具方法
2019/01/31 Python
Python PyQt5 Pycharm 环境搭建及配置详解(图文教程)
2019/07/16 Python
Python with语句和过程抽取思想
2019/12/23 Python
猎人靴英国官网:Hunter Boots
2017/02/02 全球购物
物业管理大学生个人的自我评价
2013/10/10 职场文书
高三励志标语
2014/06/05 职场文书
雷人标语集锦
2014/06/19 职场文书
2014年安全员工作总结
2014/11/13 职场文书
可可西里观后感
2015/06/08 职场文书
Python编写可视化界面的全过程(Python+PyCharm+PyQt)
2021/05/17 Python
Python操作CSV格式文件的方法大全
2021/07/15 Python
CI Games宣布《堕落之王2》使用虚幻引擎5制作 预计将于2023年正式发售
2022/04/11 其他游戏
vue ant design 封装弹窗表单的使用
2022/06/01 Vue.js