2

I have a bugs model which is mapped to categories. Categories can own other categories.

class Bug( models.Model ):
    categories = models.ManyToManyField('Category')
    name = models.CharField( max_length=100 )
    slug = models.SlugField(unique=True)
    summary = models.TextField()
    summary_markdown = models.TextField(editable=False, blank=True)
    date_added = models.DateTimeField(auto_now_add=True)
    browser = models.ManyToManyField( Browser )
    poster = models.ForeignKey(User)

class Category ( models.Model ):
    name = models.CharField( max_length=100 )
    slug = models.SlugField(unique=True)
    parent = models.ForeignKey( 'self', null=True, blank=True, related_name='children' )

    class Meta:
    ordering = ['name']
    verbose_name_plural = 'Categories'

    def __unicode__(self):
    return self.name

I am already using django-mptt on the front end view to render categories. I'm wondering how I can modify the admin view so that when I go to create a new bug, instead of a dropdown of categories in non-hierarchial order I get a hierarchial order with checkboxes instead of a dropdown.

Something like:

(checkbox) CSS
   (checkbox) Display
       (checkbox) Inline-block
       (checkbox) Block
   (checkbox) Float
   (checkbox) Height
       (checkbox) Min-height

1 Answer 1

1

I had a similar problem, but used a layout a little bit diffrent. You need to create a custom widget for displaying the categories. This is how I did it mine:

from itertools import chain

from django import forms
from django.db import models
from django.forms.widgets import CheckboxSelectMultiple
from django.utils.encoding import force_unicode
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe

class CustomCheckboxSelectMultiple(forms.CheckboxSelectMultiple):

    items_per_row = 1 # Number of items per row      

    def render(self, name, value, attrs=None, choices=()):
        if value is None: value = []
        has_id = attrs and 'id' in attrs
        final_attrs = self.build_attrs(attrs, name=name)
        output = ['<table><tr>']
        # Normalize to strings
        str_values = set([force_unicode(v) for v in value])
        for i, (option_value, option_label) in enumerate(chain(self.choices, choices)):
            # If an ID attribute was given, add a numeric index as a suffix,
            # so that the checkboxes don't all have the same ID attribute.
            if has_id:
                final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i))
                label_for = ' for="%s"' % final_attrs['id']
            else:
                label_for = ''           
                cb = forms.CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
                option_value = force_unicode(option_value)
                rendered_cb = cb.render(name, option_value)
                option_label = conditional_escape(force_unicode(option_label))
                if i != 0 and i % self.items_per_row == 0:
                    output.append('</tr><tr>')
                #Here you need to put you layout logic for display the checkboxes 
                if self.choices[i][1].parent_id is None:
                    output.append('<td nowrap><label%s>%s</label></td>' % (label_for, option_label))
                else:
                    output.append('<td nowrap><label%s>%s %s</label></td>' % (label_for, rendered_cb, option_label))

        output.append('</tr></table>')       
        return mark_safe('\n'.join(output))

In the models.py I have:

class MyModelForm(ModelForm):
    my_model_types = forms.MultipleChoiceField(label='My model types', widget = CustomCheckboxSelectMultiple, required=True)

    class Meta:
        model = MyModel                     

    def __init__(self, *args, **kwargs):
        super(self.__class__, self).__init__(*args, **kwargs)
        tree = Tree()
        tree.build_tree(reasons=MyModelType.objects.all())
        list = tree.as_list()    
        CHOICES = [(rt.pk, rt) for rt in list]             
        self.fields['reason_types'].choices = CHOICES
Sign up to request clarification or add additional context in comments.

4 Comments

Interesting. Where'd you actually store this code? models.py? Or does it not matter? And is there more to it ( calling it in another file )?
I had a separate widgets.py file that contained the widget
I didn't used mptt for storing the tree relationship. Tree class is used to parse the data from database and create a tree. The tree was then transformed in a list for displaying
Every MyModelType object contains a field named parent. This field is a reference the his parent. None means it has no parent.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.