80👍
You don’t need to create any custom field, Django already does the job, just pass the choices well formatted:
MEDIA_CHOICES = (
('Audio', (
('vinyl', 'Vinyl'),
('cd', 'CD'),
)
),
('Video', (
('vhs', 'VHS Tape'),
('dvd', 'DVD'),
)
),
)
7👍
ModelChoiceField
uses a ModelChoiceIterator
to convert the queryset to a list of choices.
You can easily override this class to introduce groups.
Here is an example that groups cities by country:
from itertools import groupby
from django.forms.models import ModelChoiceField, ModelChoiceIterator
from .models import City
class CityChoiceIterator(ModelChoiceIterator):
def __iter__(self):
queryset = self.queryset.select_related('country').order_by('country__name', 'name')
groups = groupby(queryset, key=lambda x: x.country)
for country, cities in groups:
yield [
country.name,
[
(city.id, city.name)
for city in cities
]
]
class CityChoiceField(ModelChoiceField):
iterator = CityChoiceIterator
def __init__(self, *args, **kwargs):
super().__init__(City.objects.all(), *args, **kwargs)
Note: I didn’t have time to check that this technique is compatible with the new ModelChoiceIteratorValue
introduced in Django 3.1.
- [Django]-How to use UUID
- [Django]-How can you create a non-empty CharField in Django?
- [Django]-Django – The included urlconf doesn't have any patterns in it
3👍
An extension of @Stefan Manastirliu answer to use with django-categories. (Downside is that get_tree_data()
function below allows only one level) . In combination with javascript plugins like bootstrap multiselect you can get multi-select like
Models.py
from categories.models import CategoryBase
class SampleCategory(CategoryBase):
class Meta:
verbose_name_plural = 'sample categories'
class SampleProfile(models.Model):
categories = models.ManyToManyField('myapp.SampleCategory')
forms.py
from myapp.models import SampleCategory
def get_tree_data():
def rectree(toplevel):
children_list_of_tuples = list()
if toplevel.children.active():
for child in toplevel.children.active():
children_list_of_tuples.append(tuple((child.id,child.name)))
return children_list_of_tuples
data = list()
t = SampleCategory.objects.filter(active=True).filter(level=0)
for toplevel in t:
childrens = rectree(toplevel)
data.append(
tuple(
(
toplevel.name,
tuple(
childrens
)
)
)
)
return tuple(data)
class SampleProfileForm(forms.ModelForm):
categories = forms.MultipleChoiceField(choices=get_tree_data())
class Meta:
model = SampleProfile
- [Django]-Django template can't see CSS files
- [Django]-Django Installed Apps Location
- [Django]-What is a NoReverseMatch error, and how do I fix it?
2👍
Here’s a good snippet:
Choice Field and Select Widget With Optional Optgroups:
http://djangosnippets.org/snippets/200/
- [Django]-Project design / FS layout for large django projects
- [Django]-How to mix queryset results?
- [Django]-XlsxWriter object save as http response to create download in Django
0👍
In your form.py, you need to use a ChoiceField
or a MultipleChoiceField
instead of a ModelChoiceField
if you want to use the Django built-in "choices" that natively handles the optgroup:
models.py
class Config(models.Model):
label = models.Charfield(verbose_name="Config",max_length=20,)
def __str__(self):
return self.label
class Link(models.Model):
config = models.ForeignKey(Config)
name = models.URLField(u'Name', null=True, max_length=50)
gateway = models.IPAddressField(u'Gateway', null=True)
weight = models.IntegerField(u'Weight', null=True)
description = models.TextField(u'Description', blank=True)
def __str__(self):
return self.name
forms.py
class LinkForm(ModelForm):
config = forms.ChoiceField(label="Config")
class Meta:
model = Link
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["config"].choices = [
["Configuration", [[c.id, c.label] for c in Config.objects.all()]]
]
- [Django]-Django switching, for a block of code, switch the language so translations are done in one language
- [Django]-Django tests – patch object in all tests
- [Django]-Access request in django custom template tags