출처1 : https://www.jstree.com/

출처2 : http://jsfiddle.net/6L7twnka/3/

출처3 : https://stackoverflow.com/questions/25226208/represent-directory-tree-as-json



2018/01/30 - [프로그램 자료/MS-SQL] - [MSSQL] WITH 절을 사용한 재귀쿼리_오라클 START WITH CONNECT BY




요즘 플라스크를 사용하고 있는데 jstree를 사용해서 부서를 뿌리려 한다. 

그런데 부서수가 약 5천 개 가까이 되니까 부서를 한 번에 뿌리니 6초 정도 걸리더라.


아....


그래서 jstree를 가지고 비동기로 확장할 수 있도록 수정하려고 했던 삽질기를 남긴다. 


기본적으로 jquery랑 jstree를 모두 넣어주고서 시작한다. 


화면단


<div id="container">

</div>


$('#container').jstree({

    'core': {

        'data': {

            "url": "//www.jstree.com/fiddle/?lazy",

            "data": function (node) {

                return { "id": node.id };

            }

        },

        'check_callback': true

    }

});



위의 내용이 끝이다. 

url 부분엔 기본적으로 호출 할 url를 적고 data 부분엔 무엇을 key로 ajax 호출하여 json을 받아올 것인지 넘겨줄 args 를 적는다. 


예를 들 면 처음 호출 할 때는 아래와 같이 호출되고

http://www.jstree.com/fiddle/?lazy


두번 째 자식 들을 호출 할 때는 아래와 같이 호출된다. 

http://www.jstree.com/fiddle/?lazy&id=(string)node.id



일단 내가 사용하는 부서 테이블은 ID와 부모ID를 가지고 있으며, 이것을 호출하기 위해서 재귀함수를 만들었다.

(만들고 나서 느낀건데, 어짜피 본인 기준의 1단계 자손들만 가져오기 때문에 굳이 아래 방향으로는 재귀함수가 필요 없었다. 귀찮아서 안바꾸었지만.....)


자손을 가져오는 프로시져


CREATE PROCEDURE SP_ORG_CHILDREN_S

(       

         @ORG_ID NVARCHAR(20)

)

AS

BEGIN

         -- 부서트리 가져오는 프로시저(자손)

         IF OBJECT_ID('TEMPDB..#TMP') IS NOT NULL DROP TABLE #TMP

 

         ;WITH ORG_CTE(ORG_ID, ORG_NAME, DISP_PRIORITY, ORG_PID, FULL_PATH_ID, FULL_PATH_NAME, LEVEL)

         AS

         (

                  SELECT ORG_ID, ORG_NAME, DISP_PRIORITY, ORG_PID, FULL_PATH_ID, FULL_PATH_NAME, 0

                  FROM ORG_TABLE

                  WHERE ORG_ID = @ORG_ID -- @ORG_ID 를 기준으로 아래로 1단계 만큼 자손을 가져온다.

                  UNION ALL

                  SELECT AA.ORG_ID, AA.ORG_NAME, AA.DISP_PRIORITY, AA.ORG_PID, AA.FULL_PATH_ID, AA.FULL_PATH_NAME, BB.LEVEL+1

                  FROM ORG_TABLE AS AA

                           INNER JOIN ORG_CTE AS BB ON AA.ORG_PID = BB.ORG_ID

                  WHERE BB.LEVEL < 1 -- 자손의 단계 조절

         )

         SELECT *, 'N' AS OPENED -- JSTREE의 폴더가 OPENED 열려 있을 지 정하는 부분

         INTO #TMP

         FROM ORG_CTE

 

         UPDATE #TMP SET ORG_PID = '#', OPENED = 'Y' WHERE ORG_ID = 'ROOT'

 

         SELECT A.ORG_ID, A.ORG_NAME, A.DISP_PRIORITY, A.ORG_PID, A.FULL_PATH_ID, A.FULL_PATH_NAME, A.LEVEL

                  , A.OPENED, COUNT(D.ORG_ID) AS CHILDREN --CHILDREN의 경우는 추 후 폴더 모양 앞에 삼각형(확장가능 여부)를 표기하는 데 쓰임

         FROM #TMP AS A

                  LEFT JOIN ORG_TABLE AS D ON A.ORG_ID = D.ORG_PID

         GROUP BY A.ORG_ID, A.ORG_NAME, A.DISP_PRIORITY, A.ORG_PID, A.FULL_PATH_ID, A.FULL_PATH_NAME, A.LEVEL, A.OPENED

         ORDER BY FULL_PATH_ID , LEVEL ASC

END

 



CREATE PROCEDURE SP_ORG_PARENTS_S

(       

         @ORG_ID NVARCHAR(20)

)

AS

BEGIN

         -- 부서트리 가져오는 프로시저(조상 전부)

         IF OBJECT_ID('TEMPDB..#TMP') IS NOT NULL DROP TABLE #TMP

 

         ;WITH ORG_CTE(ORG_ID, ORG_NAME, DISP_PRIORITY, ORG_PID, FULL_PATH_ID, FULL_PATH_NAME, LEVEL)

         AS

         (

                  SELECT ORG_ID, ORG_NAME, DISP_PRIORITY, ORG_PID, FULL_PATH_ID, FULL_PATH_NAME, 0

                  FROM ORG_TABLE

                  WHERE ORG_ID = @ORG_ID --@ORG_ID 를 기준으로 최상위 조상까지

                  UNION ALL

                  SELECT AA.ORG_ID, AA.ORG_NAME, AA.DISP_PRIORITY, AA.ORG_PID, AA.FULL_PATH_ID, AA.FULL_PATH_NAME, BB.LEVEL+1

                  FROM ORG_TABLE AS AA

                           INNER JOIN ORG_CTE AS BB ON AA.ORG_ID = BB.ORG_PID

         )

         SELECT B.ORG_ID, B.ORG_NAME, B.DISP_PRIORITY, B.ORG_PID, B.FULL_PATH_ID, B.FULL_PATH_NAME, A.LEVEL,

                  (CASE WHEN A.ORG_ID = B.ORG_ID THEN 'Y' ELSE 'N' END) AS OPENED -- 본인 직계 조상만 폴더를 오픈한다.

         INTO #TMP

         FROM ORG_CTE AS A

                  LEFT JOIN ORG_TABLE AS B ON A.ORG_PID LIKE B.ORG_PID

 

         UPDATE #TMP SET ORG_PID = '#' WHERE ORG_ID = 'ROOT'

 

         SELECT A.ORG_ID, A.ORG_NAME, A.DISP_PRIORITY, A.ORG_PID, A.FULL_PATH_ID, A.FULL_PATH_NAME, A.LEVEL

                  , A.OPENED, COUNT(D.ORG_ID) AS CHILDREN -- CHILDREN의 경우는 추 후 폴더 모양 앞에 삼각형(확장가능여부)를 표기하는데 쓰임

         FROM #TMP AS A

                  LEFT JOIN ORG_TABLE AS D ON A.ORG_ID = D.ORG_PID

         GROUP BY A.ORG_ID, A.ORG_NAME, A.DISP_PRIORITY, A.ORG_PID, A.FULL_PATH_ID, A.FULL_PATH_NAME, A.LEVEL, A.OPENED

         ORDER BY FULL_PATH_ID , LEVEL ASC

END

 





프로시져까지 만들었으니 이제 FLASK 에서 호출되는 함수를 보자.


부서 정보를 JSON으로 반환하는 함수



@common.route('/org/tree', methods=['GET'])

def selectorg():

    """

    부서의 트리구조를 그리기 위한 부서정보(json) 반환 함수

    """

    def _org_to_dict(dlist, id):

        """

        조회한 org tuple을 재귀형식의 json으로 변환하기 위하여 재귀형식의 dict로 변환

        """

        el = filter(lambda x : x.ORG_ID == id, dlist)[0]

 

        d = {"id" : el.ORG_ID, "text" : el.ORG_NAME,\

           "state":{"opened": True if el.OPENED == "Y" else False, "selected" : False}}

        if len(filter(lambda x : x.ORG_PID == id, dlist)) > 0:

            d['children'] = [_org_to_dict(dlist, x.ORG_ID) for x in filter(lambda x : x.ORG_PID == id, dlist)]

        else:

            d['children'] = True if el.CHILDREN > 0 else False

        return d

 

    id = None

    org_query = None

    json_data = None

    cur_org = request.args.get("cur") if "cur" in request.args else None

    if cur_org:

        org_query = db.session.execute("SP_ORG_PARENTS_S :ORG_ID",  {"ORG_ID":current_user.org_id}).fetchall()

        json_data = json.dumps(_org_to_dict(org_query, 'ROOT'))

    else:

        id = request.values.get("id") if "id" in request.values and request.values.get("id") != "#" else 'ROOT'

        org_query = db.session.execute("SP_ORG_CHILDREN_S :ORG_ID",  {"ORG_ID":id}).fetchall()

        json_data = json.dumps(_org_to_dict(org_query, id))

   

    return Response(response=json_data, status=200, mimetype="application/json")






이 상태로 잘 엮으면 폴더를 확장하면서 비동기로 데이터를 가져와 확장한다. 


화이팅!








Posted by motolies
,