简介:

无限极分类是一种比较常见的数据格式,生成组织结构,生成商品分类信息,权限管理当中的细节权限设置,都离不开无限极分类的管理。

常见的有链表式,即有一个Pid指向上级的ID,以此来设置结构。写的时候简单,用的时候效果一班,比如说,同一级没有办法手动重新排序,查询所有子孙的时候不方便。

所以有了预排序树,即左右值树形管理。

优点还是挺多的。

可以快速确定关系,最短路径,同级排序,查找所有子孙(最好的地方)

一:主要包

  1. sqlalchemy_mptt

    1. https://github.com/uralbash/sqlalchemy_mptt 
    2. pip install sqlalchemy_mptt 
  2. sqlalchemy-orm-tree

    1. https://github.com/monetizeio/sqlalchemy-orm-tree/
    2. pip install sqlalchemy-orm-tree
    3. 最后更新2015年8月27日,还没有完整的示例代码,仅有的几行,还………………
    4. 放弃
  3. ztree

    1. http://www.treejs.cn/
    2. 这个是前台JS显示用的,希望我能整合到flask-admin中去。

二:sqlalchemy_mptt

1.快速指南

源码:

# !/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time : 2018-09-12 16:32
# @Author : Jackadam
# @Email :jackadam@sina.com
# @File : mptt.py
# @Software: PyCharm
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy_mptt import mptt_sessionmaker
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy_mptt.mixins import BaseNestedSets Base = declarative_base() class Tree(Base, BaseNestedSets):
__tablename__ = "tree"
id = Column(Integer, primary_key=True)
name = Column(String(8)) def __repr__(self):
return "<Node (%s)>" % self.id engine = create_engine('sqlite:///mptt.db', echo=False)
mptt_ession = mptt_sessionmaker(sessionmaker(bind=engine))
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine)) def print_tree(group_name, tab=1):
"""
:param str group_name:要查找的树的根的名称
:param int tab: 格式化用的-数量
"""
group = db_session.query(Tree).filter_by(name=group_name).one_or_none() # type: TreeGroup
if not group:
return
# group found - print name and find children
print('- ' * tab + group.name)
for child_group in group.children: # type: TreeGroup
# new tabulation value for child record
print_tree(child_group.name, tab * 2) if __name__ == '__main__':
# Base.metadata.create_all(bind=engine)
# nodes=[]
# node=Tree(name='中国')
# nodes.append(node)
# db_session.add_all(nodes)
# db_session.commit()
# nodes = []
# ref_id=db_session.query(Tree.id).filter_by(name='中国').first()[0]
# print(ref_id)
# new_name=['河南','河北','山东','山西','陕西']
# for i in new_name:
# print(i)
# node=Tree(name=i,parent_id=ref_id)
# nodes.append(node)
# db_session.add_all(nodes)
# db_session.commit()
# nodes = []
# ref_id = db_session.query(Tree.id).filter_by(name='河南').first()[0]
# print(ref_id)
# new_name = ['郑州', '洛阳', '开封', '新乡', '新郑']
# for i in new_name:
# print(i)
# node = Tree(name=i, parent_id=ref_id)
# nodes.append(node)
# db_session.add_all(nodes)
# db_session.commit()
print_tree('中国')
print_tree('河南')

2.结果样式:

print_tree('中国')

- 中国
- - 河南
- - - - 郑州
- - - - 洛阳
- - - - 开封
- - - - 新乡
- - - - 新郑
- - 河北
- - 山东
- - 山西
- - 陕西

print_tree('河南')

- 河南
- - 郑州
- - 洛阳
- - 开封
- - 新乡
- - 新郑

3.优势

我加入的顺序可不是这个顺序,但是数据结构,和sqlalchemy_mptt帮我们处理了层级数据结构,就显示为树形结构了。

再试试下面的代码,可以列出所有的数。

def print_all_tree(tab=1):
"""
:param int tab: 格式化用的-数量
"""
group = db_session.query(Tree).filter_by(parent_id=None).all() # type: TreeGroup
print(group)
if not group:
return
# group found - print name and find children
for i in group:
print('- ' * tab + i.name)
for child_group in i.children: # type: TreeGroup
# new tabulation value for child record
print_tree(child_group.name, tab * 2)
# nodes=[]
# new_name = ['美国', '英国', '法国', '英国', '德国']
# for i in new_name:
# print(i)
# node = Tree(name=i)
# nodes.append(node)
# db_session.add_all(nodes)
# db_session.commit()
# ref_id = db_session.query(Tree.id).filter_by(name='美国').first()[0]
# nodes = []
# new_name = ['亚拉巴马', '阿拉斯加', '亚利桑那', '阿肯色', '加利福尼亚']
# for i in new_name:
# print(i)
# node = Tree(name=i,parent_id=ref_id)
# nodes.append(node)
# db_session.add_all(nodes)
# db_session.commit()
print_all_tree(1)

三:数据结构分析

数据库结构

数据库里面的内容为

映射定义结构

分析:

我们只定义了主键id,name名字。但是数据库里面多了几列:lft,rgt,level,tree_id,parent_id

这些结构就是左右值树用的东西,参照最上面的图,每个名字都有左值和右值,可以很方便的查到结构。

比如说:

找到所有的子孙:查找Food的子孙,Food作为参考点,左值为1,右值为18,所有的子孙,就是数据库中左值大于1,小于18的。

        查找Fruit的子孙,Fruit作为参考点,左值为2,右值为11,所有的子孙,就是数据库中左值大于2,小于11的。

找到所有的子节点(不包括孙节点):查找Food的子节点,Food作为参考点,level=1,tree_id=1。

        那么所有的子节点为  tree_id=1,level=1+1   层级为2的。

查找最短路径:一般用在导航中,也有用在组合显示上,因为需要知道上一级,上N级的路径结构:

        查找Banana的上级路径,Banana作为参考点,左值为8,右值为9,那么路径就是数据库中左值小于8,并且右值大于9的。排序按左值或右值的升序降序,就随便你了。

        结果是    Food--Fruit--Yellow--Banana

四:算法

1.简介:

关于预排序树的算法,有很多,我也写不好,所以我用了sqlalchemy_mptt,还是大概介绍一下吧。

2.增加

按sqlalchemy_mptt的用法,

如果没有parent_id,那么就创建为一个新树的根节点,parent_id是空的,level是1,tree_id根据数据库的情况顺序向上加。

如果有提供parent_id,那么久创建为parent的子节点,parent_id是提供的,level是parent的level+1,tree_id和parent一致。

同时要更新受影响的其他节点。

左值处理一遍。

大于parent_id右值的所有左值,+2

右值处理一遍。

大于等于parent_id右值的所有右值,+2

3.删除

和增加差不多,删除一个节点以后,也要更新受影响的左值和右值。

4.移动

这个其实就是删除一个老节点,再创建一个新节点。

5.完全不用担心

因为这些功能,sqlalchem_mptt全都实现了,根本不需要你操心。具体怎么用,我还在学习中。

下面的东西,随着我的学习,逐步更新。

五:ztree

1.ztree基础显示(带编辑功能)

下面的代码,保存为html,直接就可以了。

<!DOCTYPE html>
<HTML>
<HEAD>
<TITLE> ZTREE DEMO - beforeEditName / beforeRemove / onRemove / beforeRename / onRename</TITLE>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="../../../css/demo.css" type="text/css">
<link rel="stylesheet" href="../../../css/zTreeStyle/zTreeStyle.css" type="text/css">
<script type="text/javascript" src="../../../js/jquery-1.4.4.min.js"></script>
<script type="text/javascript" src="../../../js/jquery.ztree.core.js"></script>
<script type="text/javascript" src="../../../js/jquery.ztree.excheck.js"></script>
<script type="text/javascript" src="../../../js/jquery.ztree.exedit.js"></script>
<SCRIPT type="text/javascript">
<!--
var setting = {
view: {
addHoverDom: addHoverDom,
removeHoverDom: removeHoverDom,
selectedMulti: false
},
edit: {
enable: true,
editNameSelectAll: true,
showRemoveBtn: showRemoveBtn,
showRenameBtn: showRenameBtn
},
data: {
simpleData: {
enable: true
}
},
callback: {
beforeDrag: beforeDrag,
beforeEditName: beforeEditName,
beforeRemove: beforeRemove,
beforeRename: beforeRename,
onRemove: onRemove,
onRename: onRename
}
}; var zNodes = [
{id: 1, pId: 0, name: "父节点 1", open: true},
{id: 11, pId: 1, name: "叶子节点 1-1"},
{id: 12, pId: 1, name: "叶子节点 1-2"},
{id: 13, pId: 1, name: "叶子节点 1-3"},
{id: 2, pId: 0, name: "父节点 2", open: true},
{id: 21, pId: 2, name: "叶子节点 2-1"},
{id: 22, pId: 2, name: "叶子节点 2-2"},
{id: 23, pId: 2, name: "叶子节点 2-3"},
{id: 3, pId: 0, name: "父节点 3", open: true},
{id: 31, pId: 3, name: "叶子节点 3-1"},
{id: 32, pId: 3, name: "叶子节点 3-2"},
{id: 33, pId: 3, name: "叶子节点 3-3"}
];
var log, className = "dark"; function beforeDrag(treeId, treeNodes) {
return false;
} function beforeEditName(treeId, treeNode) {
className = (className === "dark" ? "" : "dark");
showLog("[ " + getTime() + " beforeEditName ]&nbsp;&nbsp;&nbsp;&nbsp; " + treeNode.name);
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.selectNode(treeNode);
setTimeout(function () {
if (confirm("进入节点 -- " + treeNode.name + " 的编辑状态吗?")) {
setTimeout(function () {
zTree.editName(treeNode);
}, 0);
}
}, 0);
return false;
} function beforeRemove(treeId, treeNode) {
className = (className === "dark" ? "" : "dark");
showLog("[ " + getTime() + " beforeRemove ]&nbsp;&nbsp;&nbsp;&nbsp; " + treeNode.name);
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.selectNode(treeNode);
return confirm("确认删除 节点 -- " + treeNode.name + " 吗?");
} function onRemove(e, treeId, treeNode) {
showLog("[ " + getTime() + " onRemove ]&nbsp;&nbsp;&nbsp;&nbsp; " + treeNode.name);
} function beforeRename(treeId, treeNode, newName, isCancel) {
className = (className === "dark" ? "" : "dark");
showLog((isCancel ? "<span style='color:red'>" : "") + "[ " + getTime() + " beforeRename ]&nbsp;&nbsp;&nbsp;&nbsp; " + treeNode.name + (isCancel ? "</span>" : ""));
if (newName.length == 0) {
setTimeout(function () {
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.cancelEditName();
alert("节点名称不能为空.");
}, 0);
return false;
}
return true;
} function onRename(e, treeId, treeNode, isCancel) {
showLog((isCancel ? "<span style='color:red'>" : "") + "[ " + getTime() + " onRename ]&nbsp;&nbsp;&nbsp;&nbsp; " + treeNode.name + (isCancel ? "</span>" : ""));
} function showRemoveBtn(treeId, treeNode) {
return !treeNode.isFirstNode;
} function showRenameBtn(treeId, treeNode) {
return !treeNode.isLastNode;
} function showLog(str) {
if (!log) log = $("#log");
log.append("<li class='" + className + "'>" + str + "</li>");
if (log.children("li").length > 8) {
log.get(0).removeChild(log.children("li")[0]);
}
} function getTime() {
var now = new Date(),
h = now.getHours(),
m = now.getMinutes(),
s = now.getSeconds(),
ms = now.getMilliseconds();
return (h + ":" + m + ":" + s + " " + ms);
} var newCount = 1; function addHoverDom(treeId, treeNode) {
var sObj = $("#" + treeNode.tId + "_span");
if (treeNode.editNameFlag || $("#addBtn_" + treeNode.tId).length > 0) return;
var addStr = "<span class='button add' id='addBtn_" + treeNode.tId
+ "' title='add node' onfocus='this.blur();'></span>";
sObj.after(addStr);
var btn = $("#addBtn_" + treeNode.tId);
if (btn) btn.bind("click", function () {
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.addNodes(treeNode, {id: (100 + newCount), pId: treeNode.id, name: "new node" + (newCount++)});
return false;
});
}; function removeHoverDom(treeId, treeNode) {
$("#addBtn_" + treeNode.tId).unbind().remove();
}; function selectAll() {
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.setting.edit.editNameSelectAll = $("#selectAll").attr("checked");
} $(document).ready(function () {
$.fn.zTree.init($("#treeDemo"), setting, zNodes);
$("#selectAll").bind("click", selectAll);
});
//-->
</SCRIPT>
<style type="text/css">
.ztree li span.button.add {
margin-left: 2px;
margin-right: -1px;
background-position: -144px 0;
vertical-align: top;
*vertical-align: middle
}
</style>
</HEAD> <BODY> <ul id="treeDemo" class="ztree"></ul> </BODY>
</HTML>

其中head引入了jquery,ztree的一些JS文件。

然后var setting,设置了ztree的一些参数

var zNodes,设置了一个简单结构的数结构数据。

一些function,定义了鼠标移上去,移出去,点击……的一些事件。

<style type="text/css">
.ztree li span.button.add { 这个样式,定义了添加按钮的图标。

在body中

直接  <ul id="treeDemo" class="ztree"></ul>就可以显示了

2.在flask中显示

把整页作为模板,动态传入zNodes,就可以直接显示了。

3.在flask-admin中显示

因为我这个懒蛋,准备把flask-admin当前台用。数据展示也挺方便。

先写一个页面,单独只显示ztree

视图函数:

用extra_css引入自定义css(ztree的)

用extra_js引入自定义js(ztree的)

用@expose('/')定义路由URL(flask-admin的)

tree_info,是读取数据库拿到的树的基本信息。

class User_Groups_ModelView(ModelView):
extra_css=[
"../static/zTree_v3/css/demo.css",
"../static/zTree_v3/css/zTreeStyle/zTreeStyle.css"
]
extra_js = [
"../static/zTree_v3/js/jquery.ztree.all.js",
"../static/zTree_v3/js/jquery.ztree.core.js",
"../static/zTree_v3/js/jquery.ztree.excheck.js",
"../static/zTree_v3/js/jquery.ztree.exedit.js",
"../static/zTree_v3/js/jquery.ztree.exhide.js",
# "../static/zTree_v3/js/jquery-1.4.4.min.js",
]
page_size = 20 # the number of entries to display on the list view
can_create = False
# can_edit = True
# can_delete = False
# can_view_details = True
@expose('/')
def index(self):
tree_info=get_simple_json()
return self.render('users/list.html',tree_info=tree_info)

get_simple_json就是按ztree的简单结构,生成树的基本数据。带到模板去渲染

def get_simple_json():
groups = db_session.query(User_Groups).all()
# print(len(groups))
result = []
for i in groups:
if i.parent_id == None:
i.parent_id = 0
_dict = {
'id': i.id,
'pId': i.parent_id,
'name': i.groups_name,
}
result.append(_dict)
# print(result)
return result

模板文件

前面引入了flask-admin的基本模板

block body(显示主体)

block model_menu_bar(菜单导航,就是新建--删除--导出那一行)

<script></script>中间,写了ztree的一些方法,用来动态的显示出添加,编辑,删除的小按钮。

<ul id="treeDemo" class="ztree"></ul>

这个就是显示了。

block tail

ztree的渲染,我把它写在了尾部

{% extends 'admin/master.html' %}
{% import 'admin/lib.html' as lib with context %}
{% import 'admin/actions.html' as actionslib with context %} {% block body %}
{% block model_menu_bar %}
<ul class="nav nav-tabs actions-nav">
<li class="active">
<a href="javascript:void(0)">{{ _gettext('List') }}{% if count %} ({{ count }}){% endif %}</a>
</li> {% if admin_view.can_create %}
<li>
{%- if admin_view.create_modal -%}
{{ lib.add_modal_button(url=get_url('.create_view', url=return_url, modal=True), title=_gettext('Create New Record'), content=_gettext('Create')) }}
{% else %}
<a href="{{ get_url('.create_view', url=return_url) }}"
title="{{ _gettext('Create New Record') }}">{{ _gettext('Create') }}</a>
{%- endif -%}
</li>
{% endif %} {% if admin_view.can_export %}
{{ model_layout.export_options() }}
{% endif %} {% block model_menu_bar_before_filters %}{% endblock %} {% if filters %}
<li class="dropdown">
{{ model_layout.filter_options() }}
</li>
{% endif %} {% if can_set_page_size %}
<li class="dropdown">
{{ model_layout.page_size_form(page_size_url) }}
</li>
{% endif %} {% if actions %}
<li class="dropdown">
{{ actionlib.dropdown(actions) }}
</li>
{% endif %} {% if search_supported %}
<li>
{{ model_layout.search_form() }}
</li>
{% endif %}
{% block model_menu_bar_after_filters %}{% endblock %}
</ul>
{% endblock %}
<SCRIPT type="text/javascript">
<!-- var setting = {
view: {
addHoverDom: addHoverDom,
removeHoverDom: removeHoverDom,
selectedMulti: false
},
edit: {
enable: true,
editNameSelectAll: true,
showRemoveBtn: showRemoveBtn,
showRenameBtn: showRenameBtn
},
data: {
simpleData: {
enable: true
}
},
callback: {
beforeDrag: beforeDrag,
beforeEditName: beforeEditName,
beforeRemove: beforeRemove,
beforeRename: beforeRename,
onRemove: onRemove,
onRename: onRename
}
}; var zNodes = {{ tree_info|safe }};
var log, className = "dark"; function beforeDrag(treeId, treeNodes) {
alert('这里是增加?')
return false;
} function beforeEditName(treeId, treeNode) {
className = (className === "dark" ? "" : "dark");
showLog("[ " + getTime() + " beforeEditName ]&nbsp;&nbsp;&nbsp;&nbsp; " + treeNode.name);
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.selectNode(treeNode);
setTimeout(function () {
if (confirm("进入节点 -- " + treeNode.name + " 的编辑状态吗?")) {
setTimeout(function () {
zTree.editName(treeNode);
}, 0);
}
}, 0);
return false;
} function beforeRemove(treeId, treeNode) {
className = (className === "dark" ? "" : "dark");
showLog("[ " + getTime() + " beforeRemove ]&nbsp;&nbsp;&nbsp;&nbsp; " + treeNode.name);
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.selectNode(treeNode);
return confirm("确认删除 节点 -- " + treeNode.name + " 吗?");
} function onRemove(e, treeId, treeNode) {
showLog("[ " + getTime() + " onRemove ]&nbsp;&nbsp;&nbsp;&nbsp; " + treeNode.name);
} function beforeRename(treeId, treeNode, newName, isCancel) {
className = (className === "dark" ? "" : "dark");
showLog((isCancel ? "<span style='color:red'>" : "") + "[ " + getTime() + " beforeRename ]&nbsp;&nbsp;&nbsp;&nbsp; " + treeNode.name + (isCancel ? "</span>" : ""));
if (newName.length == 0) {
setTimeout(function () {
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.cancelEditName();
alert("节点名称不能为空.");
}, 0);
return false;
}
return true;
} function onRename(e, treeId, treeNode, isCancel) {
showLog((isCancel ? "<span style='color:red'>" : "") + "[ " + getTime() + " onRename ]&nbsp;&nbsp;&nbsp;&nbsp; " + treeNode.name + (isCancel ? "</span>" : ""));
} function showRemoveBtn(treeId, treeNode) {
return !treeNode.isFirstNode;
} function showRenameBtn(treeId, treeNode) {
return !treeNode.isLastNode;
} function showLog(str) {
if (!log) log = $("#log");
log.append("<li class='" + className + "'>" + str + "</li>");
if (log.children("li").length > 8) {
log.get(0).removeChild(log.children("li")[0]);
}
} function getTime() {
var now = new Date(),
h = now.getHours(),
m = now.getMinutes(),
s = now.getSeconds(),
ms = now.getMilliseconds();
return (h + ":" + m + ":" + s + " " + ms);
} var newCount = 1; function addHoverDom(treeId, treeNode) {
var sObj = $("#" + treeNode.tId + "_span");
if (treeNode.editNameFlag || $("#addBtn_"+treeNode.tId).length>0) return;
var addStr = "<span class='button add' id='addBtn_" + treeNode.tId
+ "' title='增加节点' onfocus='this.blur();'></span>";
sObj.after(addStr);
var btn = $("#addBtn_"+treeNode.tId);
if (btn) btn.bind("click", function(){
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.addNodes(treeNode, {id:(100 + newCount), pId:treeNode.id, name:"新节点" + (newCount++)}); return false;
});
};
function removeHoverDom(treeId, treeNode) {
$("#addBtn_"+treeNode.tId).unbind().remove();
}; function selectAll() {
var zTree = $.fn.zTree.getZTreeObj("treeDemo");
zTree.setting.edit.editNameSelectAll = $("#selectAll").attr("checked");
} //-->
</SCRIPT> <ul id="treeDemo" class="ztree"></ul> {% endblock %}
{% block tail %}
<SCRIPT>
$(document).ready(function () {
$.fn.zTree.init($("#treeDemo"), setting, zNodes);
$("#selectAll").bind("click", selectAll);
});
</SCRIPT>
{% endblock %}

有一个小问题

ztree的编辑功能当中没有默认增加。所以增加这个功能写在了,默认样式表中也没有这个样式。

基础示例当中有定义这个CSS。

.ztree li span.button.add {
margin-left: 2px;
margin-right: -1px;
background-position: -144px 0;
vertical-align: top;
*vertical-align: middle
}

我就随便写进了demo.css,这样显示的就正常了。

4.定制flask-admin的显示

待编辑功能写完,再来研究这个问题。

六:ztree的编辑功能

1.简介

默认的flask-admin功能是不能管理这个树形结构的。它管理的是简单结构,就是 id pid name 这种格式。

或许可以尝试由简单格式,自动创建为左右值树格式

2.

3.

七:

八:

九:

十:

sqlalchemy tree 树形分类 无限极分类的管理。预排序树,左右值树。sqlalchemy-mptt的更多相关文章

  1. php无限极分类以及递归(thinkphp)

    php无限极分类: 无限极分类重点在于表的设计: 1在model中: class CatModel extends Model{ protected $cat = array(); public fu ...

  2. 谈一次php无限极分类的案例

    作者:白狼 出处:http://www.manks.top/php_tree_deep.html 本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追 ...

  3. PHP无限极分类生成树方法,无限分级

    你还在用浪费时间又浪费内存的递归遍历无限极分类吗,看了该篇文章,我觉得你应该换换了.这是我在OSChina上看到的一段非常精简的PHP无限极分类生成树方法,巧在引用,整理分享了. function g ...

  4. PHP无限极分类实现

    简单版的PHP生成无限极分类代码.其中包括了数据库设计.以及输出分类HTML代码. SQL代码 CREATE TABLE `district` ( `id` int(10) unsigned NOT ...

  5. PHP无限极分类生成树方法

    你还在用浪费时间又浪费内存的递归遍历无限极分类吗,看了该篇文章,我觉得你应该换换了.这是我在OSChina上看到的一段非常精简的PHP无限极分类生成树方法,整理分享了. function genera ...

  6. 分享一个牛逼的PHP无限极分类生成树方法,巧用引用(转)

    你还在用浪费时间又浪费内存的递归遍历无限极分类吗,看了该篇文章,我觉得你应该换换了.这是我在OSChina上看到的一段非常精简的PHP无限极分类生成树方法,巧在引用,整理分享了. function g ...

  7. PHP无限极分类

      当你学习php无限极分类的时候,大家都觉得一个字“难”我也觉得很难,所以,现在都还在看,因为工作要用到,所以,就必须得研究研究. 到网上一搜php无限极分类,很多,但好多都是一个,并且,写的很乱, ...

  8. PHP无限极分类,多种方法|很简单,这里说的很详细,其它地方说的很不好懂

    当你学习php无限极分类的时候,大家都觉得一个字"难"我也觉得很难,所以,现在都还在看,因为工作要用到,所以,就必须得研究研究.   到网上一搜php无限极分类,很多,但好多都是一 ...

  9. php之无限极分类

    首先建立分类信息表: CREATE TABLE IF NOT EXISTS `category` ( `categoryId` smallint(5) unsigned NOT NULL AUTO_I ...

随机推荐

  1. Linux命令echo

    echo "hello world" echo 显示字符串内容

  2. lua --- __newindex

    -- __newindex 对表进行更新 MyMetatable = {} MyTable = }, {__newindex = MyMetatable}) MyTable.newKey1 = pri ...

  3. (转)C# 之泛型详解

    什么是泛型 我们在编写程序时,经常遇到两个模块的功能非常相似,只是一个是处理int数据,另一个是处理string数据,或者其他自定义的数据类型,但我们没有办法,只能分别写多个方法处理每个数据类型,因为 ...

  4. Lasso linear model实例 | Proliferation index | 评估单细胞的增殖指数

    背景:We developed a cell-cycle scoring approach that uses expression data to compute an index for ever ...

  5. c++-pimer-plus-6th-chapter06

    Chapter Review 1 Both version give the same answers, but the if else version is more efficient. Cons ...

  6. kernel_thread简析

    1.3.100static inline pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags){    lon ...

  7. 网站访问出现 ------ Can not write to cache files, please check directory ./cache/ .

    最近在搞微商城时,突然出现了Can not write to cache files, please check directory ./cache/ .这样一个提示, 但最近好像没搞什么大动作,怎么 ...

  8. v-for

    在实际的项目中,我们很多时候会碰到将JSON数据中的数组或对象渲染出列表之类的元素.在Vue中,提供了一个 v-for的指令,可以渲染列表. 组件和v-for 在自定义组件里,你可以像任何普通元素一样 ...

  9. Tree总结

    树结构问题因为容易写出解法,因此经常出现在面试题中 1. 树的种类 1) Tree 2) Binary Trees 3) Binary Search Trees(BST) : used to sort ...

  10. 谈一谈HashMap类

    一.Java中的hashCode()和equals() 1. hashCode()的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode()是用来在散列存储结构中确定对 ...