Django模型选择选项作为多选框

14 投票
6 回答
48536 浏览
提问于 2025-04-16 03:22

假设我有这样的模型

COLORS= (
    ('R', 'Red'),
    ('B', 'Yellow'),
    ('G', 'White'),
)
class Car(models.Model):
    name = models.CharField(max_length=20)
    color= models.CharField(max_length=1, choices=COLORS)

它在管理面板中显示为一个下拉框,但我希望我的管理员用户能够像处理多对多关系那样,选择多个颜色。请问我该如何做到这一点,而不使用类似 ('RB', 'Red&Blue'), 这样的逻辑呢?

6 个回答

1

使用一个单独的表格来存储颜色(红色、蓝色、绿色),然后,正如你所说,添加一个多对多的关系?选择的类型不是多选,只是一个字符串,并且有额外的用户界面和检查。

或者,可以用itertools.combinations来生成你的选择,举个例子:

choices = zip(
  [''.join(x) for x in itertools.combinations(['','B','R','G'],2)],
  [' '.join(x) for x in itertools.combinations(['','Blue','Red','Green'],2)],
)

 # now choices = [(' Blue', 'B'), (' Red', 'R'), (' Green', 'G'), ('Blue Red', 'BR'), ('Blue Green', 'BG'), ('Red Green', 'RG')]
7

我做了一个完整的示例,里面有一些有意义的模型。它运行得非常好。我在Python 3.4.x和Django 1.8.4上测试过。

首先,我启动了管理面板,并为每个选项在Thema模型中创建了记录。

模型文件:models.py

from django.db import models

class Author(models.Model):
    fname = models.CharField(max_length=50)
    lname = models.CharField(max_length=80)

    def __str__(self):
        return "{0} {1}".format(self.fname, self.lname)


class Thema(models.Model):
    THEME_CHOICES = (
        ('tech', 'Technical'),
        ('novel', 'Novel'),
        ('physco', 'Phsycological'),
    )
    name = models.CharField(max_length=20,choices=THEME_CHOICES, unique=True)

    def __str__(self):
        return self.name

class Book(models.Model):

    name = models.CharField(max_length=50)
    author = models.ForeignKey(Author)
    themes = models.ManyToManyField(Thema)

    def __str__(self):
        return "{0} by {1}".format(self.name,self.author)

表单文件:forms.py

from django import forms

from .models import *

class BookForm(forms.ModelForm):
    themes = forms.ModelMultipleChoiceField(queryset=Thema.objects, widget=forms.CheckboxSelectMultiple(), required=False)

管理文件:admin.py

from django.contrib import admin

from .models import *
from .forms import *

@admin.register(Author)
class AuthorAdmin(admin.ModelAdmin):
    pass


@admin.register(Book)    
class BookAdmin(admin.ModelAdmin):
    form = BookForm


@admin.register(Thema)
class ThemaAdmin(admin.ModelAdmin):
    pass
12

一辆Car可以有多种color吗?如果可以的话,color就应该是一个多对多关系,而不是一个CharField。另一方面,如果你想做类似Unix权限的事情(比如红色+蓝色,红色+蓝色+绿色等等),那么给每种颜色分配一个数字值,把color设置为一个整数字段

更新

(看了评论后)你可以使用自定义表单来在管理后台编辑你的模型,而不是使用默认的ModelForm。这个自定义表单可以使用一个多选组件,让用户选择多种颜色。然后你可以重写表单的clean()方法,返回一个合适的拼接值(比如'RB'等等)。

更新 2

这里有一些代码:

首先,从模型字段中移除选项。同时把它的最大大小增加到2。我们这里不需要选项——如果需要的话,就得为每种颜色组合添加一个选项。

class Car(models.Model):
    ...
    color= models.CharField(max_length=2)

其次,添加一个自定义的ModelForm来在管理应用中使用。这个表单将重写颜色字段,并将其声明为多选字段。我们确实需要选项。

COLORS= (
    ('R', 'Red'),
    ('B', 'Yellow'),
    ('G', 'White'),
)

class CarAdminForm(ModelForm):
    color = forms.MultipleChoiceField(choices = COLORS)

    class Meta:
        model = Car

    def clean_color(self):
        color = self.cleaned_data['color']
        if not color:
            raise forms.ValidationError("...")

        if len(color) > 2:
            raise forms.ValidationError("...")

        color = ''.join(color)
        return color

注意,我只添加了一些验证。你可能想要更多的验证,或者自定义验证。

最后,将这个表单注册到管理后台。在你的admin.py文件中:

class CarAdmin(admin.ModelAdmin):
    form = CarAdminForm

admin.site.register(Car, CarAdmin)

撰写回答