Django模型MultipleChoice

5lwkijsr  于 2023-07-01  发布在  Go
关注(0)|答案(8)|浏览(118)

我知道Model没有MultipleChoiceField,只能在Forms上使用。
今天,我在分析一个与多项选择相关的新项目时遇到了一个问题。
我想有一个像CharFieldchoices与多项选择的选项字段。
我在其他时候通过创建一个CharField解决了这个问题,并使用forms.MultipleChoiceField管理表单中的多个选择,并存储以逗号分隔的选择。
在这个项目中,由于配置的原因,我不能像上面提到的那样做,我需要在模型中做,我更喜欢编辑 *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)
    ...

但有能力节省多个选择而不仅仅是一个选择。

2wnc66cl

2wnc66cl1#

您需要考虑如何在数据库级别存储数据。这将决定你的解决方案。
您可能希望在存储多个值的表中有一列。这也将迫使你考虑如何序列化-例如,如果你需要存储可能包含逗号的字符串,你不能简单地使用逗号分隔。
但是,您最好使用像django-multiselectfield这样的解决方案

brqmpdu1

brqmpdu12#

如果你正在使用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),
    )
7d7tgy0s

7d7tgy0s3#

从两者来看,https://pypi.python.org/pypi/django-select-multiple-field/看起来更圆润和完整。它甚至有一套很好的单元测试。
我发现的问题是,它在实现模型字段的类中抛出Django 1.10弃用警告。
我把这个问题解决了,发了一个PR。最新的代码,直到他们合并我的PR(如果他们决定呵呵)是在我的分支的回购,在这里:https://github.com/matiasherranz/django-select-multiple-field
干杯!
M.-

vwoqyblh

vwoqyblh4#

在您的案例中,我使用了ManyToManyField
它会是这样的:

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

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

所以,现在你可以选择多个选项

8ulbf1ek

8ulbf1ek5#

您可以使用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的所有记录。

mfpqipee

mfpqipee6#

仅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文档。:)*

kognpnkq

kognpnkq7#

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

e4eetjau

e4eetjau8#

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

相关问题