根据Django admin中的相关类别限制子类别下拉列表中的选项

时间:2010-06-04 14:48:11

标签: django django-admin

我有三个模型

class Category(models.Model):
       name = models.CharField(max_length=128)

class SubCategory(models.Model):
    category = models.ForeignKey(Category)
    name = models.CharField(max_length = 400)

class Document(models.Model):
    category = models.ForeignKey(Category, null=True,blank=True,help_text=_('Required'))
    subcategory = models.ForeignKey(SubCategory, null=True, blank=True, help_text =_('Required'))
    title = models.CharField(max_length=300)

现在在Admin界面中我有category,subcategory和title字段。 如果用户试图选择任何子类别,那么只有那个 应显示与Category相关的子类别项。 一个简单的例子是国家,州下拉列表。 我试图从Modelform中获取它,如

class DocumentAdminModelForm(ModelForm):

   def __init__(self, *args, **kwargs):    
       super(DocumentAdminModelForm, self).__init__(*args, **kwargs)
       self.fields['sub_category'] = forms.ModelChoiceField(queryset = SubCategory.objects.filter(category__id = self.fields['category'] ))

但它不起作用。我应该为此使用一些ajax和jquery吗? 还有其他方法可以建议??

由于

1 个答案:

答案 0 :(得分:0)

这似乎是实现这种关系的最佳方式,一种是父母关系模式。 这是我的解决方案,它基于原生的django字段,模板标签和管理模板的小自定义。 在示例中,我生成带有选项卡式子项的自定义选择元素(基于本机django)。 选择字段的示例(抱歉,俄语示例):

alt text http://img576.imageshack.us/img576/5269/selects.png

实现选择类(用于编辑和创建):

class mSelect(Widget):
    def __init__(self, attrs=None, choices=()):
        super(mSelect, self).__init__(attrs)
        # choices can be any iterable, but we may need to render this widget
        # multiple times. Thus, collapse it into a list so it can be consumed
        # more than once.
        self.choices = list(choices)

    def render(self, name, value, attrs=None, choices=()):

        if value is None: value = ''
        final_attrs = self.build_attrs(attrs, name=name)
        print name
        output = [u'<select name=\"%s\" style=\"width:200px;\">' % name]
        output.append(u"<option value=\"\"%s>----------</option>")
        options = self.render_options(choices, [value])
        if options:
            output.append(options)
        output.append('</select>')
        return mark_safe(u'\n'.join(output))

    def render_options(self, choices, selected_choices):

        def render_option(option_value, option_label):
            option_value = force_unicode(option_value)
            selected_html = (option_value in selected_choices) and u' selected="selected"' or u''
            return u'<option value="%s"%s style=\"padding-left:20px;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;%s</option>' % (
                escape(option_value), selected_html,
                conditional_escape(force_unicode(option_label)))
        # Normalize to strings.
        selected_choices = set([force_unicode(v) for v in selected_choices])
        output = []

        for option_value, option_label in chain(self.choices, choices):
            childs = **YOUR_MODEL**.objects.filter(parent=option_value).order_by("**YOUR_FIELDNAME**")

            if len(childs)>0:
                output.append("<option value=\"%s\" disabled >%s</option>" % (option_value,option_label))

                for child in childs:
                    output.append(render_option(child.id, child.iname))

        return u'\n'.join(output)

然后你需要为你的模型创建modelAdmin类:

示例:

class **YOUMODELADMIN**(admin.ModelAdmin):
    .....
    .....
    .....

    def formfield_for_dbfield(self, db_field, **kwargs):
        if db_field.name == '**YOUR_FIELD_NAME**':
            kwargs["widget"] = mSelect()
        field = super(**YOUMODELADMIN**, self).formfield_for_dbfield(db_field, **kwargs)  
        return field

如果您需要在admin(list_filter)中显示此关系,我认为最好的方法是为此字段编写templatetag + javascript函数以显示关系树。 实施例(图像+代码): (将文件change_list.html复制到您的模板文件夹中,如: templates / admin / App / model yourappname / templates / admin / yourmodelname / change_list.html 然后在列表过滤器块中添加模板标记的调用:

alt text http://img714.imageshack.us/img714/5141/treetd.png

Javascript块示例:

<script>
    function showTree(name_id)
    {
        if(document.getElementById("li_" + name_id).style.display=="none")
        {
            //document.getElementById("div_" + name_id).style.display = "block";
            document.getElementById("li_" + name_id).style.display = "block";
        }
        else
        {
            //document.getElementById("div_" + name_id).style.display = "none";
            document.getElementById("li_" + name_id).style.display = "none";
        }
    }
</script>

templatetag(python)的示例代码:

def YOURTEMPLATETAG(request):
    root_link = "/admin/YOURAPP/YOURMODEL/"
    mvar = "YOURFIELD_ID__id__in"
    mlink = root_link + "?"

    for item in request.GET:
        if item!=mvar:
            mlink += "&%s=%s" % (item,request.GET[item])    

    arr_options = []
    dest = HERE YOU GET YOUR ROOT OBJECTS
    selected = ""

    for item in dest:
        show = False
        childs = HERE YOU GET YOU CHILD OBJECTS

        if len(childs)>0:
            str_req = "".join("%d," % child.id for child in childs).rstrip(",")

            if u"ptype__id__in" in request.GET:
                selected = request.GET["YOURFIELDNAME__id__in"]
                if selected in str_req.split(","):
                    show = True

            proot = {"name":item.iname,"link":str_req,"childs":childs,"show":show}

            arr_options.append(proot)

    if "," not in selected and len(selected)>0:
        selected = int(selected)
    return render_to_string("PATH_TO_YOUR_TEMPLATETAG_TEMPLATE/templatetags/show_options.html",{"mlink":mlink,"selected":selected,"options":arr_options,"name":u"YOUR FILTER NAME","tst":request})

templatetag的模板示例:

<h3>{{name}}</h3>
<ul>
    <!--li class="selected"?q=-->
    <li{% ifequal selected '' %} class="selected"{% endifequal %}><a href="?">Все</a></li>
    {% for item in options %}
        <li  {% ifequal selected item.link %} class="selected"{% endifequal %} >
            <a href="{{mlink}}&YOURFIELDNAME__id__in={{item.link}}">{{item.name}}</a>
            <a href="javascript:showTree('{{item.link}}')">[show tree]</a>
        </li>

        <li id="li_{{item.link}}" {% ifequal item.show 1 %}{%else%}style="display:none;"{%endifequal%}>
            <ul>
        {% for child in item.childs %}
            <li {% ifequal selected child.id %} class="selected"{% endifequal %}><div style="margin-left:10px;"><a href="{{mlink}}&YOURFIELDNAME__id__in={{child.id}}">{{child.FIELDOFNAME}}</a></div></li>
        {% endfor %}
            </ul>
        </li>

    {% endfor %}
</ul>

最后阻止更改change_list.html:

....
....
          <div id="changelist-filter" style="width:350px;z-index:0;">
            {% load YOURTEMPLATETAGFILE %}
            {% show_date_cal request "/PATH_TO_YOUR_MODEL_VIEW/" %}
            <h2>Custom filters</h2>
            {% TEMPLATETAGNAME request %}


            <h2>{% trans 'Filter' %}</h2>
            {% for spec in cl.filter_specs %}{% admin_list_filter cl spec %}{% endfor %}
...
.....

我认为无论如何这个例子对于创建自定义控件+管理过滤器非常有用 对不起,如果没有)))