我試圖在Django中使用django-mptt創建一個菜單應用程序來創建嵌套的菜單項。在構建樹時,菜單項應按menu_order
排序。保存MPTTModel時出現AttributeError
的問題是,每當我添加嵌套的菜單項,對它們重新排序並保存菜單,引發此錯誤:
'NoneType' object has no attribute 'tree_id'
爲了能夠保存我必須手動從重建樹形菜單Django shell,並不總是有幫助,或者從子項中刪除父項關係。
從MenuItem模型中刪除order_insertion_by = ['menu_order']
時,所有內容(排序除外)均按預期工作。
models.py:
class Menu(models.Model):
POSITIONS = Choices(('header', _('Header')), ('footer', _('Footer')))
title = models.CharField(max_length=255, default='')
position = models.SlugField(choices=POSITIONS, max_length=64, default='')
def save(self, *args, **kwargs):
MenuItem.objects.rebuild()
super(Menu, self).save(*args, **kwargs)
class MenuItem(MPTTModel):
content_type = models.ForeignKey(ContentType, blank=True, null=True)
object_id = models.PositiveIntegerField(blank=True, null=True)
linked_object = generic.GenericForeignKey()
menu = models.ForeignKey('Menu', related_name='menu_items')
parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
menu_order = models.PositiveSmallIntegerField(default=0)
class MPTTMeta:
order_insertion_by = ['menu_order']
admin.py:
class MenuItemInline(admin.StackedInline):
model = MenuItem
extra = 0
sortable_field_name = 'menu_order'
autocomplete_lookup_fields = {
'generic': [['content_type', 'object_id']]
}
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
field = super(MenuItemInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
if db_field.name == 'parent':
if request._obj_ is not None:
field.queryset = field.queryset.filter(menu=request._obj_)
else:
field.queryset = field.queryset.none()
return field
class MenuAdmin(admin.ModelAdmin):
inlines = (MenuItemInline,)
def get_form(self, request, obj=None, **kwargs):
request._obj_ = obj
return super(MenuAdmin, self).get_form(request, obj, **kwargs)
admin.site.register(Menu, MenuAdmin)
回溯:
Traceback:
File "/.../django/core/handlers/base.py" in get_response
115. response = callback(request, *callback_args, **callback_kwargs)
File "/.../django/contrib/admin/options.py" in wrapper
372. return self.admin_site.admin_view(view)(*args, **kwargs)
File "/.../django/utils/decorators.py" in _wrapped_view
91. response = view_func(request, *args, **kwargs)
File "/.../django/views/decorators/cache.py" in _wrapped_view_func
89. response = view_func(request, *args, **kwargs)
File "/.../django/contrib/admin/sites.py" in inner
202. return view(request, *args, **kwargs)
File "/.../django/utils/decorators.py" in _wrapper
25. return bound_func(*args, **kwargs)
File "/.../django/utils/decorators.py" in _wrapped_view
91. response = view_func(request, *args, **kwargs)
File "/.../django/utils/decorators.py" in bound_func
21. return func(self, *args2, **kwargs2)
File "/.../django/db/transaction.py" in inner
223. return func(*args, **kwargs)
File "/.../django/contrib/admin/options.py" in change_view
1106. self.save_related(request, form, formsets, True)
File "/.../django/contrib/admin/options.py" in save_related
764. self.save_formset(request, form, formset, change=change)
File "/.../django/contrib/admin/options.py" in save_formset
752. formset.save()
File "/.../django/forms/models.py" in save
514. return self.save_existing_objects(commit) + self.save_new_objects(commit)
File "/.../django/forms/models.py" in save_existing_objects
634. saved_instances.append(self.save_existing(form, obj, commit=commit))
File "/.../django/forms/models.py" in save_existing
502. return form.save(commit=commit)
File "/.../django/forms/models.py" in save
370. fail_message, commit, construct=False)
File "/.../django/forms/models.py" in save_instance
87. instance.save()
File "/.../mptt/models.py" in save
794. self._tree_manager._move_node(self, rightmost_sibling, 'right', save=False)
File "/.../mptt/managers.py" in _move_node
414. self._make_sibling_of_root_node(node, target, position)
File "/.../mptt/managers.py" in _make_sibling_of_root_node
769. new_tree_id = getattr(right_sibling, self.tree_id_attr)
Exception Type: AttributeError at /admin/menus/menu/2/
Exception Value: 'NoneType' object has no attribute 'tree_id'
'NoneType' 指right_sibling
其是無。
原因追溯到上述三個線,其中right_sibling
設置:即使是有下一個同級
right_sibling = target.get_next_sibling()
get_next_sibling
返回無。
當重新排序最後兩個菜單項時,我有時會得到兩個根節點,它們的值爲tree_id
,lft
和rght
。這是造成get_next_sibling
功能來查詢,其中tree_id__gt=4
節點時的最後兩個節點都具有4
一個tree_id
的MenuItem對象與每個菜單對象上內嵌管理員管理。可以使用Grappelli的可排序內聯對它們進行重新排序。新創建的子節點獲得比以下根節點更高的menu_order值似乎存在問題。
我使用Python 2.7.4,Django 1.5.5和django-mptt 0.6.0。
這是django-mptt中的錯誤還是我做錯了什麼?