I have three models as
class Category(models.Model):
name = models.CharField(max_length=128)
class SubCategory(models.Model):
category = models.Forei
It seems that is the best way to realize this relations, one model with parent relation. Here is my solution which is based on native django fields,templatetags and little custom of admin template. In example, i generate custom select element with tabbed childs (based on native django). Example of select field (sorry, example in Russian language):
Realization of Select class(for edit and create):
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;\"> %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)
Then you need to create modelAdmin class for you model:
Example:
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
If you need to show this relations in admin(list_filter) i think the best way is to write templatetag for this field+javascript function to show relation tree. Example(image+code): (copy file change_list.html into your template folder like: templates/admin/App/model or yourappname/templates/admin/yourmodelname/ change_list.html Then add call of your template tag in list filter block:
Example of Javascript block:
<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>
Example code of 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})
Example of template for 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>
And finally block for 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 %}
...
.....
I think anyway this example will be useful for creating custom controls+admin filters Sorry if not )))