Django Better Admin ArrayField
django-better-admin-arrayfield provides an enhanced widget for Django's `ArrayField` within the Django administration interface. It replaces the default comma-separated input with a more user-friendly list-based interface, allowing for dynamic addition and removal of items. The library is currently at version 1.4.2, last released in December 2020, and explicitly supports Python 3.5-3.8 and Django 2.0-3.1.
Common errors
-
The 'Add another' button on the ArrayField widget doesn't appear or work in the admin.
cause Static files (JavaScript/CSS) for the custom widget are not loaded by the browser.fixRun `python manage.py collectstatic` to ensure the static assets are available and properly served. Clear browser cache if necessary. -
`ArrayField` is displayed as a simple text input or `<django.contrib.postgres.fields.array.ArrayField>` object in Django admin.
cause The `DynamicArrayMixin` was not applied to the `ModelAdmin` class, or `django_better_admin_arrayfield` is not in `INSTALLED_APPS`.fixVerify that `django_better_admin_arrayfield` is in `INSTALLED_APPS` and that your `ModelAdmin` inherits from `DynamicArrayMixin`. Ensure your model uses `ArrayField` imported from `django_better_admin_arrayfield.models.fields`. -
Inline forms containing `ArrayField` do not display the enhanced widget, or cause errors after upgrading Django.
cause The library has known compatibility issues with Django admin inlines, especially in newer Django versions (e.g., Django 4.1+).fixThis is an unresolved upstream issue. Consider refactoring your models to avoid `ArrayField` in inlines, or explore alternative form solutions for inlines that provide better `ArrayField` handling. -
TypeError: __init__() got an unexpected keyword argument 'subwidget_form' when attempting to customize the subwidget.
cause This error might occur when attempting to use a `subwidget_form` argument in an unsupported context or if there's a mismatch in the widget customization approach.fixEnsure you are following the correct `formfield_overrides` pattern as demonstrated in the library's documentation, for example: `formfield_overrides = { ArrayField: {'widget': MyCustomDynamicArrayWidget} }` where `MyCustomDynamicArrayWidget` correctly handles `subwidget_form` internally or is a direct replacement.
Warnings
- breaking The library officially supports Django versions 2.0-3.1 and Python 3.5-3.8. Using it with newer Django (4.x+) or Python (3.9+) versions may lead to unexpected behavior or breakage due to unmaintained compatibility.
- gotcha The custom `ArrayField` widget may not render or function correctly within Django admin inlines. There are existing, unresolved issues related to inline support.
- gotcha After installation and adding to `INSTALLED_APPS`, the 'Add another' button for array items might not appear or function until static files are collected.
- gotcha The library requires additional steps and modifications to function correctly within the Wagtail CMS admin interface, primarily due to differences in static file loading and icon rendering.
Install
-
pip install django-better-admin-arrayfield
Imports
- ArrayField
from django_better_admin_arrayfield.models.fields import ArrayField
- DynamicArrayMixin
from django_better_admin_arrayfield.admin.mixins import DynamicArrayMixin
- DynamicArrayTextareaWidget
from django_better_admin_arrayfield.forms.widgets import DynamicArrayTextareaWidget
Quickstart
import os
import django
from django.conf import settings
from django.core.management import call_command
# Minimal Django settings for quickstart
if not settings.configured:
settings.configure(
DEBUG=True,
INSTALLED_APPS=[
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.postgres',
'django_better_admin_arrayfield',
'my_app',
],
DATABASES={
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ':memory:',
}
},
TEMPLATES=[
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
],
STATIC_URL='/static/',
SECRET_KEY=os.environ.get('DJANGO_SECRET_KEY', 'a-very-secret-key-for-testing-only'),
MIDDLEWARE_CLASSES=[
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
],
ROOT_URLCONF='my_app.urls',
)
django.setup()
# my_app/models.py
from django.db import models
from django_better_admin_arrayfield.models.fields import ArrayField
class TaggedItem(models.Model):
name = models.CharField(max_length=255)
tags = ArrayField(models.CharField(max_length=100), blank=True, default=list)
def __str__(self):
return self.name
# my_app/admin.py
from django.contrib import admin
from django_better_admin_arrayfield.admin.mixins import DynamicArrayMixin
from .models import TaggedItem
@admin.register(TaggedItem)
class TaggedItemAdmin(admin.ModelAdmin, DynamicArrayMixin):
list_display = ('name', 'tags')
# Example of custom subwidget if needed
# from django_better_admin_arrayfield.forms.widgets import DynamicArrayTextareaWidget
# formfield_overrides = {
# ArrayField: {'widget': DynamicArrayTextareaWidget},
# }
# my_app/urls.py
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
# Example of running migrations and creating a superuser (for testing admin access)
if __name__ == '__main__':
try:
call_command('makemigrations', 'my_app')
call_command('migrate')
print("Django setup complete. Access admin at /admin/")
except Exception as e:
print(f"Error during Django setup: {e}")
# You can now run the Django development server using: python manage.py runserver
# (assuming you have a manage.py set up to use these settings)