【问题标题】:Django Model MultipleChoiceDjango 模型多项选择
【发布时间】:2015-02-10 23:59:59
【问题描述】:

我知道 Model 没有MultipleChoiceField,您只能在表单上使用它。

今天我在分析一个与多项选择相关的新项目时遇到了一个问题。

我想要一个像 CharFieldchoices 这样的字段,并且可以选择多项。

我在其他时候通过创建CharField 解决了这个问题,并使用forms.MultipleChoiceField 管理表单中的多个选项,并存储用逗号分隔的选项。

在这个项目中,由于配置的原因,我不能像上面提到的那样做,我需要在模型中做,我更喜欢 NOT 来编辑 Django 管理表单 都不使用表格。 我需要一个具有多项选择的模型字段

  • 有人通过模型解决过这样的问题吗?

可能会覆盖某些模型函数或使用自定义小部件...我不知道,我有点迷路了。


编辑

我知道简单选择,我想有类似的东西:

class MODEL(models.Model):
    MY_CHOICES = (
        ('a', 'Hola'),
        ('b', 'Hello'),
        ('c', 'Bonjour'),
        ('d', 'Boas'),
    )
    ...
    ...
    my_field = models.CharField(max_length=1, choices=MY_CHOICES)
    ...

但可以保存多项选择,而不仅仅是一项选择。

【问题讨论】:

  • 您所描述的应该是两个模型之间的多对多关系,至少如果您关心您的数据库架构健全性的话。而且实现起来会比编写自定义字段和小部件更快...
  • @brunodesthuilliers 不,我对链接 2 模型不感兴趣,事实上,没有多个选项的模型(我在我的问题中从未说过)。我需要选项就像一个选择,一个元组。
  • @Liarez:在关系模式中,字段应该是原子的。这就是为什么技术上合理且正确的解决方案是使用您的选项创建第二个模型并使用多对多关系。请注意,整个过程大约需要 10 分钟。
  • @brunodesthuilliers 再次,也许我解释得不好,我不是英语本地人,由于项目本身,我需要避免使用模型,我知道你说的选项是正确的一,是任何人都会想到的第一个,但我不能那样做(因为项目结构,但主要是因为客户)

标签: python django django-models


【解决方案1】:

您需要考虑如何在数据库级别存储数据。这将决定您的解决方案。

大概,您希望表中的单个列存储多个值。这也将迫使您考虑如何进行序列化 - 例如,如果您需要存储可能包含逗号的字符串,则不能简单地使用逗号分隔。

但是,您最好使用django-multiselectfield 之类的解决方案

【讨论】:

  • django-multiselectfield 正是我想要的。我没有看到其他人的问题!非常感谢您提供这些信息!
  • 我来自未来,感谢您的回答!多选字段就像魅力一样!
  • 你可以使用多选字段并保持多对多关系吗?
  • Pypi 昨天因维护而停机,链接再次正常工作。但我还是将它们更新为现代风格。
  • @BigMonkey89WithaLeg 你知道它是否可以吗?
【解决方案2】:

如果您使用 Postgres,请考虑使用 ArrayField

from django.db import models
from django.contrib.postgres.fields import ArrayField

class WhateverModel(models.Model):
    WHATEVER_CHOICE = u'1'
    SAMPLE_CHOICES = (
        (WHATEVER_CHOICE, u'one'),
    )
    choices = ArrayField(
        models.CharField(choices=SAMPLE_CHOICES, max_length=2, blank=True, default=WHATEVER_CHOICE),
    )

【讨论】:

  • 上述内容不适用于单个字段中的漂亮下拉菜单。正确的?在这种情况下,有没有办法让下拉菜单出现(可以根据需要添加额外的实例,每个实例都有自己的下拉菜单)
  • @DeanKayton 您需要一个自定义表单为管理员分配 TypedMultipleChoiceField 到字段 class UserForm(forms.ModelForm): roles = TypedMultipleChoiceField(choices=User.ROLES) 然后在您的管理员上设置 form=UserForm
  • 谢谢@GordonWrigley。最后,我们决定引用多对多关系并使用 admin.py (admin.ModelAdmin) autocomplete_fields 会更简单,如果 autocomplete_fields 也可以简单地用于硬编码选择。
【解决方案3】:

从两者来看,https://pypi.python.org/pypi/django-select-multiple-field/ 看起来更加圆润和完整。它甚至还有一组不错的单元测试。

我发现的问题是它在实现模型字段的类中抛出了 Django 1.10 deprecation 警告。

我解决了这个问题并发送了 PR。最新的代码,直到他们合并我的 PR(如果他们决定呵呵)在我的 repo 的分支中,这里:https://github.com/matiasherranz/django-select-multiple-field

干杯!

M.-

【讨论】:

  • 您好,欢迎来到 SO。这不是论坛,而是问答网站。我建议使用该站点的tour。您已经发布了应该是原始问题的答案,但事实并非如此;这是对 spookylukey 答案的评论。或者,如果它真的是一个答案,并且只是基于另一个答案,那就应该说清楚。所以请edit它。谢谢!
  • 也许是真的,它无论如何都有助于桌面上的主题。鉴于我的声望不超过50点,我无法回复spookylukey的评论,这是我的初衷。如果您认为为开源做出贡献并在这里分享不值得,那么请继续禁止我的评论。祝你有美好的一天!
【解决方案4】:

在你的情况下,我使用了 ManyToManyField

会是这样的:

class MY_CHOICES(models.Model)
    choice = models.CharField(max_length=154, unique=True)

class MODEL(models.Model):
    ...
    ...
    my_field = models.ManyToManyField(MY_CHOICES)

所以,现在你可以多选了

【讨论】:

  • 这太疯狂了,但效果很好。我从 2010 年开始使用它。我还没有发现任何错误。只需要确保用户从 UI 中只选择一个字段。但缺点是,当您有可数的静态值 Salutations、Genders 等时,它并不方便。在这种情况下,我更喜欢将它们放在 UI 上并使用 JS 处理而不是创建模型
  • 没关系,但是当您想将字段留给客户从选择中添加他想要的任何内容时,情况会有所不同。 UI 中的 JS 是否可用?
  • -是的,当然。你可以用 JavaScript 做任何事情。通过登录到 admin ui 来查看 django admin 的 ManyToManyField 选项,他们已经在那儿做了。他们通过 django 本身处理它。如果您使用 jquery、anugular 或任何 javascript,它可能会更加灵活和用户友好。
【解决方案5】:

如果您希望小部件看起来像一个文本输入,并且仍然能够允许从建议中选择多个选项,您可能正在寻找Select2。还有 django-select2 将其与 Django Forms 和 Admin 集成。

【讨论】:

    【解决方案6】:

    您可以使用IntegerField 作为模型,使用2 的幂作为选项(位图字段)。我不确定为什么 Django 没有内置这个。

    class MyModel(models.Model):
        A = 1
        B = 2
        C = 4
        MY_CHOICES = ((A, "foo"), (B, "bar"), (C, "baz"))
        my_field = models.IntegerField(default=0)
    
    
    from functools import reduce
    
    
    class MyForm(forms.ModelForm):
        class Meta:
            model = MyModel
        
        # it can be set to required=True if needed
        my_multi_field = forms.TypedMultipleChoiceField(
            coerce=int, choices=MyModel.MY_CHOICES, required=False)
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.fields['my_multi_field'].initial = [
                c for c, _ in MyModel.MY_CHOICES
                if self.instance.my_field & c
            ]
    
        def save(self, *args, **kwargs):
            self.instance.my_field = reduce(
                lambda x, y: x | y,
                self.cleaned_data.get('my_multi_field', []),
                0)
            return super().save(*args, **kwargs)
    

    可以这样查询:MyModel.objects.filter(my_field=MyModel.A | MyModel.C)获取所有设置了A和C的记录。

    【讨论】:

      【解决方案7】:

      仅限 Postgres

      很晚了,但对于那些遇到这个的人 基于@lechup 的回答我遇到了this gist。 看看那个要点那里有更多改进的版本

      from django import forms
      from django.contrib.postgres.fields import ArrayField
      
      
      class ChoiceArrayField(ArrayField):
          """
          A field that allows us to store an array of choices.
          
          Uses Django 1.9's postgres ArrayField
          and a MultipleChoiceField for its formfield.
          
          Usage:
              
              choices = ChoiceArrayField(models.CharField(max_length=...,
                                                          choices=(...,)),
                                         default=[...])
          """
      
          def formfield(self, **kwargs):
              defaults = {
                  'form_class': forms.MultipleChoiceField,
                  'choices': self.base_field.choices,
              }
              defaults.update(kwargs)
              # Skip our parent's formfield implementation completely as we don't
              # care for it.
              # pylint:disable=bad-super-call
              return super(ArrayField, self).formfield(**defaults)
      

      然后我在我的另一个项目的另一个生产代码中看到了它。它运行得非常好,以至于我认为它来自 Django 的默认字段。 我在谷歌上搜索只是为了找到我来到这里的 Django 文档。 :)

      【讨论】:

        【解决方案8】:

        我找到的最简单的方法(只是我使用 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()
                    ...
        

        【讨论】:

          猜你喜欢
          • 2011-07-08
          • 1970-01-01
          • 2021-12-24
          • 1970-01-01
          • 1970-01-01
          • 2017-11-20
          • 1970-01-01
          • 1970-01-01
          • 2018-09-15
          相关资源
          最近更新 更多