15
For OP, or anyone out there looking, between these helpful bits you should be good to go:
43
I take no credit for this (original source), but if you are using PostgreSQL as the database and are happy to use the Postgres-specific ArrayField implementation there is an even easier option: subclass ArrayField
on the model and override the default admin widget. A basic implementation follows (tested in 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]-'RelatedManager' object is not iterable Django
- [Django]-405 POST method not allowed
- [Django]-How do I convert datetime.timedelta to minutes, hours in Python?
10
This is a better version of an already accepted solution. Using "CheckboxSelectMultiple" makes it more usable in the admin page.
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]-"Post Image data using POSTMAN"
- [Django]-Django β Rollback save with transaction atomic
- [Django]-Find Monday's date with Python
10
The Django better admin ArrayField package provides exactly this functionality. The advantage over the solutions above is that it allows you to add new entries dynamically instead of relying on pre-defined choices.
See the documentation here: django-better-admin-arrayfield
It has a drop-in replacement for the ArrayField
and a simple mixin to add to the admin model.
# 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):
...
This would show something like:
- [Django]-How to render menu with one active item with DRY?
- [Django]-How to handle per object permission in Django nowadays?
- [Django]-'function' object has no attribute 'as_view'
4
This is another version using the Django Admin M2M filter_horizontal
widget, instead of the standard HTML select multiple.
We use Django forms only in the Admin site, and this works for us, but the admin widget FilteredSelectMultiple
probably will break if used outside the Admin. An alternative would be overriding the ModelAdmin.get_form
to instantiate the proper form class and widget for the array field. The ModelAdmin.formfields_overrides
is not enough because you need to instantiate the widget setting the positional arguments as shown in the code snippet.
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)
- [Django]-Django: Converting an entire set of a Model's objects into a single dictionary
- [Django]-Django ImportError: cannot import name 'render_to_response' from 'django.shortcuts'
- [Django]-Django / Comet (Push): Least of all evils?
3
django-select2
offers a way to render the ArrayField
using Select2. In their documentation, the example is for ArrayField
:
To render the already selected values:
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)
To add the widget to your form:
class MyForm(ModelForm):
class Meta:
fields = ['my_array_field']
widgets = {
'my_array_field': ArrayFieldWidget
}
- [Django]-Set language within a django view
- [Django]-Django's SuspiciousOperation Invalid HTTP_HOST header
- [Django]-ReactJS with Django β real usage
0
write a form class for your model and use forms.MultipleChoiceField for ArrayField:
class ModelForm(forms.ModelForm):
my_array_field = forms.MultipleChoiceField(
choices=[1, 2, 3]
)
class Meta:
exclude = ()
model = Model
use ModelForm in your admin class:
class ModelAdmin(admin.ModelAdmin):
form = ModelForm
exclude = ()
fields = (
'my_array_field',
)
- [Django]-How does Django's nested Meta class work?
- [Django]-How to reset db in Django? I get a command 'reset' not found error
- [Django]-OSError: [Errno 18] Invalid cross-device link