[Django]-Django admin choice field

28👍

from django.contrib import admin
from django import forms

class MyModel(MyBaseModel):
    stuff = models.CharField('Stuff', max_length=255, default=None)

    class Meta:
        proxy = True

class MyModelForm(forms.ModelForm):
    MY_CHOICES = (
        ('A', 'Choice A'),
        ('B', 'Choice B'),
    )

    stuff = forms.ChoiceField(choices=MY_CHOICES)

class MyModelAdmin(admin.ModelAdmin):
    fields = ('stuff',)
    list_display = ('stuff',)
    form = MyModelForm

admin.site.register(MyModel, MyModelAdmin)

See: https://docs.djangoproject.com/en/dev/ref/forms/fields/#choicefield

👤demux

7👍

You don’t need a custom form.

This is the minimum you need:

# models.py
from __future__ import unicode_literals

from django.db import models

class Photo(models.Model):
    CHOICES = (
        ('hero', 'Hero'),
        ('story', 'Our Story'),
    )

    name = models.CharField(max_length=250, null=False, choices=CHOICES)

# admin.py
from django.contrib import admin
from .models import Photo


class PhotoAdmin(admin.ModelAdmin):
    list_display = ('name',)


admin.site.register(Photo, PhotoAdmin)
👤daino3

5👍

You need to override the form the ModelAdmin is going to use:

class MyForm(forms.ModelForm):
    stuff = forms.CharField('Stuff', max_length=255, choices=MY_CHOICES, default=None)

    class Meta:
        model = MyModel
        fields = ('stuff', 'other_field', 'another_field')


class MyModelAdmin(admin.ModelAdmin):
    fields = ('stuff',)
    list_display = ('stuff',)
    form = MyForm

If you need your choices to be dynamic, maybe you could do something similar to:

class MyForm(forms.ModelForm):
    stuff = forms.CharField('Stuff', max_length=255, choices=MY_CHOICES, default=None)

    def __init__(self, stuff_choices=(), *args, **kwargs):
        # receive a tupple/list for custom choices
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['stuff'].choices = stuff_choices

and in your ModelAdmin‘s __init__ define what MY_CHOICES is going to be and assign the form instance there instead:

Good luck! 🙂

👤Gerard

5👍

You can override formfield_for_choice_field() that way you don’t need to create a new form.

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_choice_field(self, db_field, request, **kwargs):
        if db_field.name == 'status':
            kwargs['choices'] = (
                ('accepted', 'Accepted'),
                ('denied', 'Denied'),
            )
            if request.user.is_superuser:
                kwargs['choices'] += (('ready', 'Ready for deployment'),)
        return super().formfield_for_choice_field(db_field, request, **kwargs)

See formfield_for_choice_field

0👍

in Gerard’s answer, if you keep :

def __init__(self, stuff_choices=(), *args, **kwargs):

then when you will try to add new model from admin, you will always get ‘This field is required.’ for all required fields.

you should remove stuff_choices=() from initialization:

def __init__(self,*args, **kwargs):

0👍

You need to think of how you are going to store the data at a database level.
I suggest doing this:

  1. Run this pip command: pip install django-multiselectfield
  2. In your models.py file:

    from multiselectfield import MultiSelectField
    
    MY_CHOICES = (('item_key1', 'Item title 1.1'),
              ('item_key2', 'Item title 1.2'),
              ('item_key3', 'Item title 1.3'),
              ('item_key4', 'Item title 1.4'),
              ('item_key5', 'Item title 1.5'))
    
    class MyModel(models.Model):
          my_field = MultiSelectField(choices=MY_CHOICES)
    
  3. In your settings.py:

     INSTALLED_APPS = (
          'django.contrib.auth',
          'django.contrib.contenttypes',
          'django.contrib.sessions',
          'django.contrib.sites',
          'django.contrib.admin',
    
          #.....................#
    
          'multiselectfield',
    )
    
  4. Watch the MAGIC happen!

Source:

0👍

Below a solution that works immediately with Postgres’ special ArrayField:

# models.py

class MyModel(models.Model):
    class Meta:
        app_label = 'appname'

    name = models.CharField(max_length=1000, blank=True)
  
    ROLE_1 = 'r1'
    ROLE_2 = 'r2'
    ROLE_3 = 'r3'

    ROLE_CHOICES = (
        (ROLE_1, 'role 1 name'),
        (ROLE_2, 'role 2 name'),
        (ROLE_3, 'role 3 name'),
    )

    roles = ArrayField(
        models.CharField(choices=ROLE_CHOICES, max_length=2, blank=True),
        default=list
    )

# admin.py

class MyModelForm(ModelForm):
    roles = MultipleChoiceField(choices=MyModel.ROLE_CHOICES, widget=CheckboxSelectMultiple)


@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
    form = MyModelForm
    list_display = ("pk", "name", "roles")

(Django 2.2)

Leave a comment