django 非常实用的无限级分类功能


利用model中的save方法,变相的实现递归循环,所有子分类都能在其中更新,感觉挺巧妙,之前的实现方式确实太烂了。    order的方法竟然支持1:23:2 这种方式的排序,轻松解决以前靠order_id排序的弊端。精简了代码。

其中一断代码: 利用reverse 方法反推url,与无限级的order 还能用在自动生成类别链接上,便捷灵活。

 

def htmlpath ( self ): 
        paths  =  [ ] 
         for p  in  self. path. split ( ':' ): 
            c  = ArticleCategory. objects. get (id__exact =p ) 
            url  = reverse ( 'cms.article.list' , kwargs = { 'cid':c. id } ) 
            paths. append ( '<a href="%s" target="_blank">%s</a>' %  (url , c. name ) ) 
         return  " > ". join (paths )
from django. db. models. signals  import pre_save 

class ArticleCategory (models. Model ): 
    name  = models. CharField (max_length = 50 ) 
    parent  = models. ForeignKey ( 'self' , null = True , blank = True , related_name = 'children' )     
    path  = models. CharField (max_length = 255 ,  null = True , blank = True ) 


     def  __unicode__ ( self ): 
         if  self. id  ==  self. path: 
             return  self. name 
         else: 
             return  self. node 

     
     def _node ( self ): 
        indent_num  =  len ( self. path. split ( ':' ) ) - 1 
        indent  =  '....' * indent_num 
        node  = u '%s%s' %  (indent ,  self. name ) 
         return node 
    node  =  property (_node ) 


     class Meta: 
        ordering  =  [ 'path' ] 


     #设置在model中的用途是,是在所有节点保存时递归的循环下去,更新所有的节点的路径 
     def save ( self , * args , ** kwargs ): 
         super (ArticleCategory , self ). save (*args , ** kwargs ) 

         if  self. parent: 
             self. path  =  '%s:%s' %  ( self. parent. path ,  self. id ) 
         else: 
             self. path  =  self. id 

        childrens  =  self. children. all ( ) 
         if  len (childrens )  >  0: 
             for children  in childrens: 
                children. path  =  '%s:%s' %  ( self. path , children. id ) 
                children. save ( ) 
         super (ArticleCategory , self ). save (*args , ** kwargs ) 

#信号触发,更新 
def inital_articlecategory_path (sender , instance ,  **kwargs ): 
     if instance. id: 
         if instance. parent: 
            instance. path  =  '%s:%s' %  (instance. parent. path , instance. id ) 
         else: 
            instance. path  = instance. id 
pre_save. connect (inital_articlecategory_path , sender =ArticleCategory )

admin.py

class ArticleCategoryAdmin (admin. ModelAdmin ): 
    list_display  =  [ 'treenode' , 'patha' , 'id' ,  ] 
    ordering  =  [ 'path' ] 

     def patha ( self , obj ): 
         if obj. parent: 
             return u '%s > %s' %  (obj. parent , obj. name ) 
         return obj. name 

    patha. short_description  =  'path' 
    patha. allow_tags  =  True 
     

     def treenode ( self , obj ): 
        indent_num  =  len (obj. path. split ( ':' ) ) - 1 
        p  =  '<div style="text-indent:%spx;">%s</div>' %  (indent_num* 25 , obj. name ) 
         return p 

    treenode. short_description  =  'tree path' 
    treenode. allow_tags  =  True 
admin. site. register (ArticleCategory , ArticleCategoryAdmin )

分析代码后,发现该方法可以不使用signals 来实现,在path变换后 再次运行 super(ArticleCategory,self).save(*args, ** kwargs) ,这样在children中才能在新的循环save中更新path时变更正确,否则path保存时会异常。

这个是不使用signals的代码,依靠model的save的实现。

class ArticleCategory (models. Model ): 
    name  = models. CharField (max_length = 50 ) 
    parent  = models. ForeignKey ( 'self' , null = True , blank = True , related_name = 'children' )     
    path  = models. CharField (max_length = 255 ,  null = True , blank = True ) 


     def  __unicode__ ( self ): 
         if  self. id  ==  self. path: 
             return  self. name 
         else: 
             return  self. node 

     
     def _node ( self ): 
        indent_num  =  len ( self. path. split ( ':' ) ) - 1 
        indent  =  '....' * indent_num 
        node  = u '%s%s' %  (indent ,  self. name ) 
         return node 
    node  =  property (_node ) 


     class Meta: 
        ordering  =  [ 'path' ] 


     #设置在model中的用途是,是在所有节点保存时递归的循环下去,更新所有的节点的路径 
     def save ( self , * args , ** kwargs ): 
         #先保存数据,如果是新添加的数据,放在第一行是用来获得id,因为id是path的重要组成 
         super (ArticleCategory , self ). save (*args , ** kwargs ) 
         if  self. parent: 
             self. path  =  '%s:%s' %  ( self. parent. path ,  self. id ) 
         else: 
             self. path  =  self. id 

         #更新完当前节点path后,要进行一次保存,否则在编辑类别时,子分类循环保存父类path不是最新的 
         super (ArticleCategory , self ). save (*args , ** kwargs ) 

        childrens  =  self. children. all ( ) 
         if  len (childrens )  >  0: 
             for children  in childrens: 
                 
                children. path  =  '%s:%s' %  ( self. path , children. id ) 

                children. save ( )

参考:http://www.open-open.com/code/view/1453477576558


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM