更好的 ArrayField 管理小部件?
Better ArrayField admin widget?
有什么方法可以让 ArrayField 的管理小部件允许添加和删除对象?似乎默认情况下,它只显示一个文本字段,并对其值使用逗号分隔。
除了不方便之外,AFAICT 在数组的基字段是 Char/TextField 的情况下,不允许以任何方式在数组的任何文本中包含逗号。
对于 OP 或任何正在寻找的人来说,在这些有用的位之间你应该好好去:
django-select2
提供了一种使用 Select2 呈现 ArrayField
的方法。在他们的文档中,该示例适用于 ArrayField
:
要呈现已选择的值:
class ArrayFieldWidget(Select2TagWidget):
def render_options(self, *args, **kwargs):
try:
selected_choices, = args
except ValueError: # Signature contained `choices` prior to Django 1.10
choices, selected_choices = args
output = ['<option></option>' if not self.is_required and not self.allow_multiple_selected else '']
selected_choices = {force_text(v) for v in selected_choices.split(',')}
choices = {(v, v) for v in selected_choices}
for option_value, option_label in choices:
output.append(self.render_option(selected_choices, option_value, option_label))
return '\n'.join(output)
def value_from_datadict(self, data, files, name):
values = super().value_from_datadict(data, files, name)
return ",".join(values)
要将小部件添加到您的表单:
class MyForm(ModelForm):
class Meta:
fields = ['my_array_field']
widgets = {
'my_array_field': ArrayFieldWidget
}
我对此不以为然 (original source),但如果您使用 PostgreSQL 作为数据库并且乐于使用 Postgres 特定的 ArrayField 实现,则有一个更简单的选择:子类 ArrayField
在模型上并覆盖默认的管理小部件。基本实现如下(在 Django 1.9、1.10、1.11、2.0、2.1 和 2.2 中测试):
models.py
from django import forms
from django.db import models
from django.contrib.postgres.fields import ArrayField
class ChoiceArrayField(ArrayField):
"""
A field that allows us to store an array of choices.
Uses Django's Postgres ArrayField
and a MultipleChoiceField for its formfield.
"""
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)
FUNCTION_CHOICES = (
('0', 'Planning'),
('1', 'Operation'),
('2', 'Reporting'),
)
class FunctionModel(models.Model):
name = models.CharField(max_length=128, unique=True)
function = ChoiceArrayField(
base_field=models.CharField(max_length=256, choices=FUNCTION_CHOICES),
default=list)
这是另一个使用 Django Admin M2M filter_horizontal
小部件的版本,而不是标准 HTML select 多个。
我们仅在管理站点中使用 Django 表单,这对我们有用,但如果在管理站点之外使用,管理小部件 FilteredSelectMultiple
可能会损坏。另一种方法是覆盖 ModelAdmin.get_form
以实例化适当的形式 class 和数组字段的小部件。 ModelAdmin.formfields_overrides
是不够的,因为您需要实例化设置位置参数的小部件,如代码片段所示。
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.contrib.postgres.fields import ArrayField
from django.forms import MultipleChoiceField
class ChoiceArrayField(ArrayField):
"""
A choices ArrayField that uses the `horizontal_filter` style of an M2M in the Admin
Usage::
class MyModel(models.Model):
tags = ChoiceArrayField(
models.TextField(choices=TAG_CHOICES),
verbose_name="Tags",
help_text="Some tags help",
blank=True,
default=list,
)
"""
def formfield(self, **kwargs):
widget = FilteredSelectMultiple(self.verbose_name, False)
defaults = {
"form_class": MultipleChoiceField,
"widget": widget,
"choices": self.base_field.choices,
}
defaults.update(kwargs)
# Skip our parent's formfield implementation completely as we don't
# care for it.
return super(ArrayField, self).formfield(**defaults)
为您的模型写一个表格 class 并为 ArrayField 使用 forms.MultipleChoiceField:
class ModelForm(forms.ModelForm):
my_array_field = forms.MultipleChoiceField(
choices=[1, 2, 3]
)
class Meta:
exclude = ()
model = Model
在您的管理中使用 ModelForm class:
class ModelAdmin(admin.ModelAdmin):
form = ModelForm
exclude = ()
fields = (
'my_array_field',
)
这是一个已被接受的解决方案的改进版本。使用“CheckboxSelectMultiple”使其在管理页面中更有用。
class ChoiceArrayField(ArrayField):
def formfield(self, **kwargs):
defaults = {
'form_class': forms.TypedMultipleChoiceField,
'choices': self.base_field.choices,
'coerce': self.base_field.to_python,
'widget': forms.CheckboxSelectMultiple,
}
defaults.update(kwargs)
return super(ArrayField, self).formfield(**defaults)
Django 更好的管理 ArrayField 包提供了这个功能。与上述解决方案相比的优势在于,它允许您动态添加新条目,而不是依赖于预定义的选择。
在此处查看文档:django-better-admin-arrayfield
它有一个直接替代 ArrayField
和一个简单的 mixin 来添加到管理模型。
# models.py
from django_better_admin_arrayfield.models.fields import ArrayField
class MyModel(models.Model):
my_array_field = ArrayField(models.IntegerField(), null=True, blank=True)
# admin.py
from django_better_admin_arrayfield.admin.mixins import DynamicArrayMixin
@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin, DynamicArrayMixin):
...
这将显示如下内容:
有什么方法可以让 ArrayField 的管理小部件允许添加和删除对象?似乎默认情况下,它只显示一个文本字段,并对其值使用逗号分隔。
除了不方便之外,AFAICT 在数组的基字段是 Char/TextField 的情况下,不允许以任何方式在数组的任何文本中包含逗号。
对于 OP 或任何正在寻找的人来说,在这些有用的位之间你应该好好去:
django-select2
提供了一种使用 Select2 呈现 ArrayField
的方法。在他们的文档中,该示例适用于 ArrayField
:
要呈现已选择的值:
class ArrayFieldWidget(Select2TagWidget):
def render_options(self, *args, **kwargs):
try:
selected_choices, = args
except ValueError: # Signature contained `choices` prior to Django 1.10
choices, selected_choices = args
output = ['<option></option>' if not self.is_required and not self.allow_multiple_selected else '']
selected_choices = {force_text(v) for v in selected_choices.split(',')}
choices = {(v, v) for v in selected_choices}
for option_value, option_label in choices:
output.append(self.render_option(selected_choices, option_value, option_label))
return '\n'.join(output)
def value_from_datadict(self, data, files, name):
values = super().value_from_datadict(data, files, name)
return ",".join(values)
要将小部件添加到您的表单:
class MyForm(ModelForm):
class Meta:
fields = ['my_array_field']
widgets = {
'my_array_field': ArrayFieldWidget
}
我对此不以为然 (original source),但如果您使用 PostgreSQL 作为数据库并且乐于使用 Postgres 特定的 ArrayField 实现,则有一个更简单的选择:子类 ArrayField
在模型上并覆盖默认的管理小部件。基本实现如下(在 Django 1.9、1.10、1.11、2.0、2.1 和 2.2 中测试):
models.py
from django import forms
from django.db import models
from django.contrib.postgres.fields import ArrayField
class ChoiceArrayField(ArrayField):
"""
A field that allows us to store an array of choices.
Uses Django's Postgres ArrayField
and a MultipleChoiceField for its formfield.
"""
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)
FUNCTION_CHOICES = (
('0', 'Planning'),
('1', 'Operation'),
('2', 'Reporting'),
)
class FunctionModel(models.Model):
name = models.CharField(max_length=128, unique=True)
function = ChoiceArrayField(
base_field=models.CharField(max_length=256, choices=FUNCTION_CHOICES),
default=list)
这是另一个使用 Django Admin M2M filter_horizontal
小部件的版本,而不是标准 HTML select 多个。
我们仅在管理站点中使用 Django 表单,这对我们有用,但如果在管理站点之外使用,管理小部件 FilteredSelectMultiple
可能会损坏。另一种方法是覆盖 ModelAdmin.get_form
以实例化适当的形式 class 和数组字段的小部件。 ModelAdmin.formfields_overrides
是不够的,因为您需要实例化设置位置参数的小部件,如代码片段所示。
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.contrib.postgres.fields import ArrayField
from django.forms import MultipleChoiceField
class ChoiceArrayField(ArrayField):
"""
A choices ArrayField that uses the `horizontal_filter` style of an M2M in the Admin
Usage::
class MyModel(models.Model):
tags = ChoiceArrayField(
models.TextField(choices=TAG_CHOICES),
verbose_name="Tags",
help_text="Some tags help",
blank=True,
default=list,
)
"""
def formfield(self, **kwargs):
widget = FilteredSelectMultiple(self.verbose_name, False)
defaults = {
"form_class": MultipleChoiceField,
"widget": widget,
"choices": self.base_field.choices,
}
defaults.update(kwargs)
# Skip our parent's formfield implementation completely as we don't
# care for it.
return super(ArrayField, self).formfield(**defaults)
为您的模型写一个表格 class 并为 ArrayField 使用 forms.MultipleChoiceField:
class ModelForm(forms.ModelForm):
my_array_field = forms.MultipleChoiceField(
choices=[1, 2, 3]
)
class Meta:
exclude = ()
model = Model
在您的管理中使用 ModelForm class:
class ModelAdmin(admin.ModelAdmin):
form = ModelForm
exclude = ()
fields = (
'my_array_field',
)
这是一个已被接受的解决方案的改进版本。使用“CheckboxSelectMultiple”使其在管理页面中更有用。
class ChoiceArrayField(ArrayField):
def formfield(self, **kwargs):
defaults = {
'form_class': forms.TypedMultipleChoiceField,
'choices': self.base_field.choices,
'coerce': self.base_field.to_python,
'widget': forms.CheckboxSelectMultiple,
}
defaults.update(kwargs)
return super(ArrayField, self).formfield(**defaults)
Django 更好的管理 ArrayField 包提供了这个功能。与上述解决方案相比的优势在于,它允许您动态添加新条目,而不是依赖于预定义的选择。
在此处查看文档:django-better-admin-arrayfield
它有一个直接替代 ArrayField
和一个简单的 mixin 来添加到管理模型。
# models.py
from django_better_admin_arrayfield.models.fields import ArrayField
class MyModel(models.Model):
my_array_field = ArrayField(models.IntegerField(), null=True, blank=True)
# admin.py
from django_better_admin_arrayfield.admin.mixins import DynamicArrayMixin
@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin, DynamicArrayMixin):
...
这将显示如下内容: