Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django ModelForm foreign key optgroup select

I'm building a simple Q&A website with Django 1.8.
I want to create select optgroup based on foreing key (details below).

How can I do that?

College

class College(models.Model):
    title = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    slug = models.SlugField(blank=True, max_length=100)
    university = models.ForeignKey(to=University, related_name='college_list')

University

class University(models.Model):
    title = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    slug = models.SlugField(unique=True, blank=True, max_length=100)

Question

class Question(models.Model):
    title = models.CharField(max_length=150)
    body = RedactorField(verbose_name=u'Vsebina')
    slug = models.SlugField(unique=True, blank=True, max_length=100)
    college = models.ForeignKey(to=College, default=1, related_name='questions')

QuestionForm

class CreateQuestionForm(ModelForm):
    class Meta:
        model = Question
        fields = ['title', 'body', 'college']

Template (displaying the select)

{{ form.college }}

Current result
enter image description here

Needed result
enter image description here

Thanks!

like image 391
intelis Avatar asked Jul 06 '15 22:07

intelis


1 Answers

I know this is old, but I have hit upon a solution to this problem of "displaying ModelChoiceField options categorised by a ForeignKey to another model".

Django added the ability to provide nested optgroups in a ChoiceField a few years back. However there is no automatic way to build nested choices like this from a queryset of nested model instanes.

I managed to solve it using OrderedDict to organise the subgroups into groups, then set the choices to the .items() generator. It's important to realise that you're dealing with dynamic choices here as the University and College model instances could change over time.

QuestionForm:

from collections import OrderedDict
from django.core.exceptions import ObjectDoesNotExist

class CreateQuestionForm(ModelForm):
    """
    ModelForm which dynamically builds a nested set of choices for University and College
    """

    class Meta:
        model = Question
        fields = ['title', 'body', 'college']

    def __init__(self, *args, **kwargs):
        super(CreateQuestionForm, self).__init__(*args, **kwargs)  # Sets up the fields

        university_college_choices_dict = OrderedDict()  # Abused to sort into groups
        for college in self.fields["college"].queryset.order_by("university__title", "college__title"):
            choice_tuple = (college.pk, college.title)
            try:
                university_name = college.university.title
            except (AttributeError, ObjectDoesNotExist):
                university_name = False  # Ends up in the generic ungrouped bin
            try:
                university_college_choices_dict[university_name].append(choice_tuple)
            except KeyError:
                university_college_choices_dict[university_name] = [choice_tuple]

        self.fields["college"].choices = university_college_choices_dict.items()  # MUST call .items() to extract the nested tuples
like image 85
michaeljtbrooks Avatar answered Oct 27 '22 12:10

michaeljtbrooks