Note: must call within init() method, so you must do self.fields["field"] = ModelChoiseField(...). This is because I did not use a ModelChoiceIterator.
A subclass of ModelChoiceField which represents the tree level of each node when generating option labels. It's limited to one level of nesting, if you need more, you should consider the django-mptt package.
For example, where a form which used a ModelChoiceField:
category = ModelChoiceField(queryset=Category.objects.all())
...would result in a select with the following options:
---------
Root 1
Root 2
Child 1.1
Child 1.2
Child 2.1
Using a NestedModelChoiceField instead:
category = NestedModelChoiceField(queryset=Category.objects.all(),
related_name='category_set',
parent_field='parent_id',
label_field='title')
...would result in a select with the following options:
Root 1
--- Child 1.1
--- Child 1.2
Root 2
--- Child 2.1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #-*- coding: utf-8 -*-
from django import forms
from django.utils.html import conditional_escape, mark_safe
from django.utils.encoding import smart_text
class NestedModelChoiceField(forms.ModelChoiceField):
"""A ModelChoiceField that groups parents and childrens"""
def __init__(self, related_name, parent_field, label_field, *args, **kwargs):
"""
@related_name: related_name or "FOO_set"
@parent_field: ForeignKey('self') field, use 'name_id' to save some queries
@label_field: field for obj representation
ie:
class MyModel(models.Model):
parent = models.ForeignKey('self', null=True, blank=True)
title = models.CharField()
field = NestedModelChoiceField(queryset=MyModel.objects.all(),
related_name='mymodel_set',
parent_field='parent_id',
label_field='title')
"""
super(NestedModelChoiceField, self).__init__(*args, **kwargs)
self.related_name = related_name
self.parent_field = parent_field
self.label_field = label_field
self._populate_choices()
def _populate_choices(self):
# This is *hackish* but simpler than subclassing ModelChoiceIterator
choices = []
kwargs = {self.parent_field: None, }
queryset = self.queryset.filter(**kwargs)\
.prefetch_related(self.related_name)
for parent in queryset:
choices.append((self.prepare_value(parent), self.label_from_instance(parent)))
choices.extend([(self.prepare_value(children), self.label_from_instance(children))
for children in getattr(parent, self.related_name).all()])
self.choices = choices
def label_from_instance(self, obj):
level_indicator = ""
if getattr(obj, self.parent_field):
level_indicator = "--- "
return mark_safe(level_indicator + conditional_escape(smart_text(getattr(obj, self.label_field))))
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 1 week ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 2 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 6 months ago
Comments
Please login first before commenting.