如何在Django-admin中显示自定义嵌套复选框树视图?

时间:2022-12-26 07:22:00

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

我有一个bug模型,它映射到类别。类别可以拥有其他类别。

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.

我已经在前端视图上使用了django-mptt来呈现类别。我想知道如何修改管理视图,以便当我去创建一个新的bug时,而不是在非层次顺序下拉目录中,我可以得到一个带有复选框的层次顺序,而不是下拉。

Something like:

喜欢的东西:

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

1 个解决方案

#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:

在模型。py我有:

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

#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:

在模型。py我有:

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