I have a Django application and want to display multiple choice checkboxes in an Django's admin interface. I do not want to create a separate model for my choices by using a ManyToManyField.
models.py
from django.db import models
STAFF_BUSINESS_TYPES = {
(1, "Foo"),
(2, "Bar"),
(3, "Cat"),
(4, "Dog")
}
class Business(models.Model):
name = models.CharField(max_length=255, unique=True)
business_types = models.CommaSeparatedIntegerField(max_length=32, choices=STAFF_BUSINESS_TYPES)
forms.py
from business.models import Business, STAFF_BUSINESS_TYPES
from django.forms import CheckboxSelectMultiple, ModelForm, MultipleChoiceField
class BusinessForm(ModelForm):
business_types = MultipleChoiceField(required=True, widget=CheckboxSelectMultiple, choices=STAFF_BUSINESS_TYPES)
class Meta:
model = Business
fields = ['name', 'business_types']
def clean_business_types(self):
data = self.cleaned_data['business_types']
cleaned_data = ",".join(data)
return cleaned_data
admin.py
from django.contrib import admin
from business.models import Business
from business.forms import BusinessForm
@admin.register(Business)
class BusinessAdmin(admin.ModelAdmin):
form = BusinessForm
However, when I attempt to add a business with type "Bar":
Select a valid choice. 1 is not one of the available choices.
Likewise with when I attempt to add a business with multiple values selected:
Select a valid choice. 1,2 is not one of the available choices.
How is 1 not a valid choice, considering (1, "Foo") is within my choices? Is it invalid to use Django's built in Comma Separated Integer field like this?
In a nutshell: Make a form for each model, submit them both to template in a single <form> , using prefix keyarg and have the view handle validation. If there is dependency, just make sure you save the "parent" model before dependant, and use parent's ID for foreign key before commiting save of "child" model.
A widget is Django's representation of an HTML input element. The widget handles the rendering of the HTML, and the extraction of data from a GET/POST dictionary that corresponds to the widget. The HTML generated by the built-in widgets uses HTML5 syntax, targeting <! DOCTYPE html> .
You can override forms for django's built-in admin by setting form attribute of ModelAdmin to your own form class. See: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.form.
The similarities are that they both generate sets of form inputs using widgets, and both validate data sent by the browser. The differences are that ModelForm gets its field definition from a specified model class, and also has methods that deal with saving of the underlying model to the database. Save this answer.
I've worked in a similar problem and here goes my solution:
# coding: utf-8
# python2 / django1.6.5
"""
That's a first solution I got to use CommaSeparatedIntegerFields with SelectMultiple widget.
My intension is to change this solution to a custom Widget that inherits from SelectMultiple.
*** It still needs refactoring ***
"""
from django.db import models
MY_CHOICES = (
(1, 'escolha1'),
(2, 'escolha2'),
(3, 'escolha3'),
(4, 'escolha4'),
(5, 'escolha5'),
)
class MeuConteudo(models.Model):
meu_campo = models.CommaSeparatedIntegerField(
blank=True, max_length=255,
)
from django import forms
from minhaapp.models import MeuConteudo, MY_CHOICES
class CommaSeparatedSelectInteger(forms.MultipleChoiceField):
def to_python(self, value):
if not value:
return ''
elif not isinstance(value, (list, tuple)):
raise ValidationError(
self.error_messages['invalid_list'], code='invalid_list'
)
return ','.join([str(val) for val in value])
def validate(self, value):
"""
Validates that the input is a string of integers separeted by comma.
"""
if self.required and not value:
raise ValidationError(
self.error_messages['required'], code='required'
)
# Validate that each value in the value list is in self.choices.
for val in value.split(','):
if not self.valid_value(val):
raise ValidationError(
self.error_messages['invalid_choice'],
code='invalid_choice',
params={'value': val},
)
def prepare_value(self, value):
""" Convert the string of comma separated integers in list"""
return value.split(',')
class MeuConteudoMultipleForm(forms.ModelForm):
meu_campo = CommaSeparatedSelectInteger(
choices=MY_CHOICES,
widget=forms.SelectMultiple
)
class Meta:
model = MeuConteudo
from forms import MeuConteudoMultipleForm
from minhaapp.models import MeuConteudo
from minhaapp.forms import MeuConteudoMultipleForm
class MeuConteudoAdmin(admin.ModelAdmin):
form = MeuConteudoMultipleForm
admin.site.register(MeuConteudo, MeuConteudoMultipleForm)
https://gist.github.com/romulocollopy/bffe38fa72af5bc427e1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With