【问题标题】:django model choice option as a multi select boxdjango 模型选择选项作为多选框
【发布时间】:2010-08-27 08:41:35
【问题描述】:

假设我有这样的模型

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'), 类型的逻辑,如何实现这一点

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    我已经用有意义的模型构建了一个完整的工作示例。它工作完美。我已经在 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
    

    【讨论】:

    • 与接受的答案不同(在 Django 2.1/Python 3.7 中,它会给你错误django.core.exceptions.ImproperlyConfigured: Creating a ModelForm without either the 'fields' attribute or the 'exclude' attribute is prohibited; form CarAdminForm needs updating.),这个答案仍然非常有效。 +1
    【解决方案2】:

    一个Car 可以有多个colors 吗?在这种情况下,color 应该是 many to many relationship 而不是 CharField。另一方面,如果您想做Unix permissions 之类的事情(即红+蓝、红+蓝+绿等),则为它们中的每一个分配数字值并将color 设为integer field

    更新

    (阅读评论后)您可以使用custom form 在管理员中编辑您的模型,而不是默认的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)
    

    【讨论】:

    • 实际上我的例子很糟糕,我已经更新了我的颜色以获得更好的说明。
    • @Hellnar:如果您的管理员用户选择了多种颜色,您打算如何存储它们?还是您只想要一个多选框,即使您只想存储一种颜色?
    【解决方案3】:

    使用带有颜色(红色、蓝色、绿色)的单独表格,并且如您所说,添加多对多关系? 选择类型不是多项选择,只是一个添加了 UI 和检查的字符串。

    或者,使用 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')]
    

    【讨论】:

    • 或者,更好的是,choices = sum([[(' '.join(x),''.join(i[0] for i in x)) for x in itertools.combinations([ 'Blue','Red','Green'],n)] for n in (1,2,3)],[])
    【解决方案4】:

    对于颜色元组,如果您使用整数而不是字符,您可以为您的模型使用 commaseparatedintegerfield。 但是不要忘记,逗号分隔的整数字段是数据库级别的结构,所以你的 DBMS 必须支持它。

    Documentation link...

    【讨论】:

      【解决方案5】:

      我找到的最简单的方法(只是我使用 eval() 将从输入获取的字符串转换为元组以再次读取表单实例或其他地方)

      这个技巧效果很好

      #model.py
      class ClassName(models.Model):
          field_name = models.CharField(max_length=100)
      
          def __init__(self, *args, **kwargs):
              super().__init__(*args, **kwargs)
              if self.field_name:
                  self.field_name= eval(self.field_name)
      
      
      
      #form.py
      CHOICES = [('pi', 'PI'), ('ci', 'CI')]
      
      class ClassNameForm(forms.ModelForm):
          field_name = forms.MultipleChoiceField(choices=CHOICES)
      
          class Meta:
              model = ClassName
              fields = ['field_name',]
      
      #view.py
      def viewfunction(request, pk):
          ins = ClassName.objects.get(pk=pk)
      
          form = ClassNameForm(instance=ins)
          if request.method == 'POST':
              form = form (request.POST, instance=ins)
              if form.is_valid():
                  form.save()
                  ...
      

      【讨论】:

        【解决方案6】:

        Django 模型带有 JSONField() ,您可以使用它来序列化整数值列表,然后将其反序列化为列表。整数列表是你的颜色。

        https://docs.djangoproject.com/en/4.0/ref/models/fields/#django.db.models.JSONField

        【讨论】:

          猜你喜欢
          • 2015-02-10
          • 2011-02-13
          • 1970-01-01
          • 2011-07-08
          • 2017-11-20
          • 2016-03-22
          • 1970-01-01
          • 2013-02-14
          • 1970-01-01
          相关资源
          最近更新 更多