• 카테고리

    질문 & 답변
  • 세부 분야

    풀스택

  • 해결 여부

    해결됨

(게시판 댓글구현 오류) count 부분에서 오류가 나는데.. 어떻게 해야할까요..ㅠ?

22.04.24 01:12 작성 조회수 437

2

안녕하세요

남박사님

 

남박사님 덕분에 강의를 잘듣고 진도를 나가고 있습니다. 이해가 안가는 부분도 아직 있지만 열심히 배울려고 노력하고 있는데요!

이번 댓글기능 만들기 부분에서 오류나는 부분있어서 질문올립니다.

 

댓글기능 구연하고 마지막 부분에서 jinja2.exceptions.UndefinedError: 'pymongo.cursor.Cursor object' has no attribute 'count'라고 오류가 나는데.. 저번에 {% if comments.count() > 0 %} 이부분이 몽고db 부분하고 연동이 안되는거 같아서 버전 차이라고 하셔서 count_documents({조건})로 변경해봤는데.. 오류는 나더라구요..!

 

- 오류 -

 

 

-코드 -

-board.py-

 

from main import *
from flask import Blueprint, send_from_directory
from flask import send_from_directory

bluerprint = Blueprint("board", __name__, url_prefix="/board")

def board_delete_attach_file(filename):
    abs_path = os.path.join(app.config["BOARD_ATTACH_FILE_PATH"], filename)
    if os.path.exists(abs_path):
        os.remove(abs_path)
        return True
    return False


@bluerprint.route("/comment_write", methods=["POST"])
@login_required
def comment_write():
    if request.method == "POST":
        print("POST OK")
        name = session.get("name")
        writer_id = session.get("id")
        root_idx = request.form.get("root_idx")
        comment = request.form.get("comment")
        current_utc_time = round(datetime.utcnow().timestamp() * 1000)
        c_comment = mongo.db.comment
        post = {
            "root_idx": str(root_idx),
            "writer_id": writer_id,
            "name": name,
            "comment": comment,
            "pubdate": current_utc_time
        }

        print(post)

        c_comment.insert_one(post)
        return redirect(url_for("board.board_view", idx=root_idx))
   

@bluerprint.route("/upload_image", methods=["POST"])
def upload_image():
    if request.method == "POST":
        file = request.files["image"]
        if file and allowed_file(file.filename):
            filename = "{}.jpg".format(rand_generator())
            savefilepath = os.path.join(app.config["BOARD_IMAGE_PATH"], filename)
            file.save(savefilepath)
            return url_for("board.board_images", filename=filename)


@bluerprint.route("/images/<filename>")
def board_images(filename):
    return send_from_directory(app.config["BOARD_IMAGE_PATH"], filename)


@bluerprint.route("/files/<filename>")
def board_files(filename):
    return send_from_directory(app.config["BOARD_ATTACH_FILE_PATH"], filename, as_attachment=True)


@bluerprint.route("/list")
def lists():
    # 페이지 값 (값이 없는 경우 기본값는 1)
    page = request.args.get("page", 1, type=int)
    # 한페이지당 몇개의 게시물을 출력할지
    limit = request.args.get("limit", 5, type=int)

    search = request.args.get("search", -1, type=int)
    keyword = request.args.get("keyword", "", type=str)

    # 최종적으로 완성된 쿼리를 만들 변수
    query = {}
    # 검색어 상태를 추가할 리스트 변수
    search_list = []

    if search == 0:
        search_list.append({"title": {"$regex": keyword}})
    elif search == 1:
        search_list.append({"contents": {"$regex": keyword}})
    elif search == 2:
        search_list.append({"title": {"$regex": keyword}})
        search_list.append({"contents": {"$regex": keyword}})
    elif search == 3:
        search_list.append({"name": {"$regex": keyword}})

    # 검색 대상이 한개라도 존재할 경우 query 변수에 $or 리스트를 쿼리 합니다.
    if len(search_list) > 0:
        query = {"$or": search_list}

    print(query)

    board = mongo.db.board
    datas = board.find({}).skip(
        (page - 1) * limit).limit(limit).sort("pubdate", -1)

    # 게시물의 총 갯수
    tot_count = board.count_documents({})
    # 마지막 페이지의 수를 구한다.
    last_page_num = math.ceil(tot_count / limit)
    # 페이지 블럭을 5개씩 표기
    block_size = 5
    # 현재 블럭의 위치
    block_num = int((page - 1) / block_size)
    # 블럭의 시작 위치
    block_start = int((block_size * block_num) + 1)
    # 블럭의 끝 위치
    block_last = math.ceil(block_start + (block_size - 1))

    return render_template(
        "list.html",
        datas=list(datas),
        limit=limit,
        page=page,
        block_start=block_start,
        block_last=block_last,
        last_page_num=last_page_num,
        search=search,
        keyword=keyword,
        title="게시판 리스트")


@bluerprint.route("/view/<idx>")
@login_required
def board_view(idx):
    # idx = request.args.get("idx")
    if idx is not None:
        page = request.args.get("page")
        search = request.args.get("search")
        keyword = request.args.get("keyword")

        board = mongo.db.board
        # data = board.find_one({"_id": ObjectId(idx)})
        data = board.find_one_and_update({"_id": ObjectId(idx)}, {
            "$inc": {"view": 1}}, return_document=True)

        if data is not None:
            result = {
                "id": data.get("_id"),
                "name": data.get("name"),
                "title": data.get("title"),
                "contents": data.get("contents"),
                "pubdate": data.get("pubdate"),
                "view": data.get("view"),
                "writer_id": data.get("writer_id", ""),
                "attachfile": data.get("attachfile", "")
            }

            comment = mongo.db.comment
            comments = comment.find({"root.idx": str(data.get("_id"))})
            return render_template("view.html", result=result, comments=comments, page=page, search=search, keyword=keyword, title="글 상세보기")
    return abort(404)


@bluerprint.route("/write", methods=["GET", "POST"])
def board_write():
    if request.method == "POST":
        filename = None
        if "attachfile" in request.files:
            file = request.files["attachfile"]
            if file and allowed_file(file.filename):
                filename = check_filename(file.filename)
                file.save(os.path.join(app.config['BOARD_ATTACH_FILE_PATH'], filename))

        name = request.form.get("name")
        title = request.form.get("title")
        contents = request.form.get("contents")

        request.files
        current_utc_time = round(datetime.utcnow().timestamp() * 1000)
        board = mongo.db.board
        post = {
            "name": name,
            "title": title,
            "contents": contents,
            "pubdate": current_utc_time,
            "writer_id": session.get("id"),
            "view": 0,
        }
       
        if filename is not None:
            post["attachfile"] = filename

        x = board.insert_one(post)
        print(x.inserted_id)
        return redirect(url_for("board.board_view", idx=x.inserted_id))
    else:
        return render_template("write.html", title="글 작성")


@bluerprint.route("/edit/<idx>", methods=["GET", "POST"])
def board_edit(idx):
    if request.method == "GET":
        board = mongo.db.board
        data = board.find_one({"_id": ObjectId(idx)})
        if data is None:
            flash("해당 게시물이 존재하지 않습니다.")
            return redirect(url_for("board.lists"))
        else:
            if session.get("id") == data.get("writer_id"):
                return render_template("edit.html", data=data, title="글 수정")
            else:
                flash("글 수정 권한이 없습니다.")
                return redirect(url_for("board.lists"))
    else:
        title = request.form.get("title")
        contains = request.form.get("contents")
        deleteoldfile = request.form.get("deleteoldfile", "")

        board = mongo.db.board
        data = board.find_one({"_id": ObjectId(idx)})
        if session.get("id") == data.get("writer_id"):
            filename = None
            if "attachfile" in request.files:
                file = request.files["attachfile"]
                if file and allowed_file(file.filename):
                    filename = check_filename(file.filename)
                    file.save(os.path.join(app.config["BOARD_ATTACH_FILE_PATH"], filename))

                    if data.get("attachfile"):
                        board_delete_attach_file(data.get("attachfile"))
                else:
                    if deleteoldfile == "on":
                        filename = None
                        if data.get("attachfile"):
                            board_delete_attach_file(data.get("attachfile"))
                    else:
                        filename = data.get("attachfile")

            board.update_one({"_id": ObjectId(idx)}, {
                "$set": {
                    "title": title,
                    "contents": contains,
                    "attachfile": filename
                }
            })
            flash("수정 되었습니다.")
            return redirect(url_for("board.board_view", idx=idx))
        else:
            flash("글 수정 권한이 없습니다.")
            return redirect(url_for("board.lists"))


@bluerprint.route("/delete/<idx>")
def board_delete(idx):
    board = mongo.db.board
    data = board.find_one({"_id": ObjectId(idx)})
    if data.get("writer_id") == session.get("id"):
        board.delete_one({"_id": ObjectId(idx)})
        flash("삭제 되었습니다.")
    else:
        flash("삭제 권한이 없습니다.")
    return redirect(url_for("board.lists"))
   
 
 
-------------------------------------------------------------------------------------------------------
 
 
 
- view.html-
 
 
 
{% extends "main.html" %}

{% block contents %}

<div style="padding: 50px 50px 50px 50px;">
    <table class="table table-bordered">
        <tbody>
            <tr>
                <td colspan="2">{{result.title}}</td>
            </tr>
            <tr>
                <td>{{result.name}}</td>
                <td class="text-right">{{result.pubdate|formatdatetime}}</td>
            </tr>
            {% if result.attachfile %}
            <tr>
                <td>첨부파일</td>
                <td><a href="{{url_for('board.board_files', filename=result.attachfile)}}">{{result.attachfile}}</a></td>
            </tr>
            {% endif %}
            <tr>
                <td colspan="2"><div style="min-height: 200px;"></div>{% autoescape false %}{{result.contents}}{% endautoescape %}</td>
            </tr>
        </tbody>
    </table>

    <a class="btn btn-primary" href="{{url_for('board.lists', page=page, search=search, keyword=keyword)}}">리스트</a>

    {% if session["id"] == result.writer_id %}
    <a class="btn btn-danger float-right ml-2" href="{{url_for('board.board_delete', idx=result.id)}}">글삭제</a>
    <a class="btn btn-warning float-right" href="{{url_for('board.board_edit', idx=result.id)}}">글수정</a>
    {% endif %}
    <br>
    <br>
    <form id="commentForm" name="commentForm" action="{{url_for('board.comment_write')}}" method="POST">
        <input type="hidden" name="csrf_token" value="{{csrf_token()}}">
        <input type="hidden" name="root_idx" value="{{result.id}}">
        <div>
            <span><strong>댓글</strong></span> <span id="cCnt"></span>
            <table class="table">
                <tr>
                    <td><textarea rows="3" cols="110" id="comment" name="comment" placeholder="댓글을 입력하세요"></textarea></td>
                    <td><input type="submit" class="btn btn-success" style="height: 80px;" value="등록하기"></td>
                </tr>
            </table>
        </div>
    </form>

    {% if comments.count() > 0 %}
        {% for c in comments %}
            <div>
                <table class="table">
                    <tr>
                        <td width="100"><h6>{{c.name}}</h6></td>
                        <td>{{c.comment}}</td>
                        <td class="text-right" width="200">{{c.pubdate | formatdatetime}}</td>
                    </tr>
                </table>
            </div>
        {% endfor %}
    {% endif %}
</div>

{% endblock %}
 

 

 

답변 2

·

답변을 작성해보세요.

0

 

comment = mongo.db.comment
comments = comment.find({"root.idx": str(data.get("_id"))})
return render_template("view.html", result=result, comments=list(comments), page=page, search=search, keyword=keyword, title="글 상세보기")

제가 생각을 잘못했습니다. comments 를 넘길때 먼저 위 코드처럼 list로 형변환을 해서 넘기고,

{% if comments|length > 0 %} 

HTML 의 jinja2 에서는 위 코드처럼 length 로 접근하게 수정해보시기 바랍니다.

문신호님의 프로필

문신호

질문자

2022.04.25

남박사님 답변 주셔서 감사합니다!

오류났던 부분은 해결했는데요. view.html에 댓글이 생기지는 않는데.. 어떻게 해야할까요?

몽고DB에는 내용은 저장이 됩니다!

 

그리고 리스트에서 글작성을 쓴다음에 작성하기 누르기 전 summernote 에디터 창에서는 글작성이 정상적으로 동작되는데.. 저장하고 나면 글 작성내용이

하단부로 내려가던데 이건 왜그런걸까요?

 

- 이미지 - 1 - 

 

- 이미지 - 2-

 

 

- 코드 -

 

- board.py -

 

from main import *
from flask import Blueprint, send_from_directory
from flask import send_from_directory

bluerprint = Blueprint("board", __name__, url_prefix="/board")

def board_delete_attach_file(filename):
    abs_path = os.path.join(app.config["BOARD_ATTACH_FILE_PATH"], filename)
    if os.path.exists(abs_path):
        os.remove(abs_path)
        return True
    return False


@bluerprint.route("/comment_write", methods=["POST"])
@login_required
def comment_write():
    if request.method == "POST":
        print("POST OK")
        name = session.get("name")
        writer_id = session.get("id")
        root_idx = request.form.get("root_idx")
        comment = request.form.get("comment")
        current_utc_time = round(datetime.utcnow().timestamp() * 1000)
        c_comment = mongo.db.comment
        post = {
            "root_idx": str(root_idx),
            "writer_id": writer_id,
            "name": name,
            "comment": comment,
            "pubdate": current_utc_time
        }

        print(post)

        c_comment.insert_one(post)
        return redirect(url_for("board.board_view", idx=root_idx))
   

@bluerprint.route("/upload_image", methods=["POST"])
def upload_image():
    if request.method == "POST":
        file = request.files["image"]
        if file and allowed_file(file.filename):
            filename = "{}.jpg".format(rand_generator())
            savefilepath = os.path.join(app.config["BOARD_IMAGE_PATH"], filename)
            file.save(savefilepath)
            return url_for("board.board_images", filename=filename)


@bluerprint.route("/images/<filename>")
def board_images(filename):
    return send_from_directory(app.config["BOARD_IMAGE_PATH"], filename)


@bluerprint.route("/files/<filename>")
def board_files(filename):
    return send_from_directory(app.config["BOARD_ATTACH_FILE_PATH"], filename, as_attachment=True)


@bluerprint.route("/list")
def lists():
    # 페이지 값 (값이 없는 경우 기본값는 1)
    page = request.args.get("page", 1, type=int)
    # 한페이지당 몇개의 게시물을 출력할지
    limit = request.args.get("limit", 5, type=int)

    search = request.args.get("search", -1, type=int)
    keyword = request.args.get("keyword", "", type=str)

    # 최종적으로 완성된 쿼리를 만들 변수
    query = {}
    # 검색어 상태를 추가할 리스트 변수
    search_list = []

    if search == 0:
        search_list.append({"title": {"$regex": keyword}})
    elif search == 1:
        search_list.append({"contents": {"$regex": keyword}})
    elif search == 2:
        search_list.append({"title": {"$regex": keyword}})
        search_list.append({"contents": {"$regex": keyword}})
    elif search == 3:
        search_list.append({"name": {"$regex": keyword}})

    # 검색 대상이 한개라도 존재할 경우 query 변수에 $or 리스트를 쿼리 합니다.
    if len(search_list) > 0:
        query = {"$or": search_list}

    print(query)

    board = mongo.db.board
    datas = board.find({}).skip(
        (page - 1) * limit).limit(limit).sort("pubdate", -1)

    # 게시물의 총 갯수
    tot_count = board.count_documents({})
    # 마지막 페이지의 수를 구한다.
    last_page_num = math.ceil(tot_count / limit)
    # 페이지 블럭을 5개씩 표기
    block_size = 5
    # 현재 블럭의 위치
    block_num = int((page - 1) / block_size)
    # 블럭의 시작 위치
    block_start = int((block_size * block_num) + 1)
    # 블럭의 끝 위치
    block_last = math.ceil(block_start + (block_size - 1))

    return render_template(
        "list.html",
        datas=list(datas),
        limit=limit,
        page=page,
        block_start=block_start,
        block_last=block_last,
        last_page_num=last_page_num,
        search=search,
        keyword=keyword,
        title="게시판 리스트")


@bluerprint.route("/view/<idx>")
@login_required
def board_view(idx):
    # idx = request.args.get("idx")
    if idx is not None:
        page = request.args.get("page")
        search = request.args.get("search")
        keyword = request.args.get("keyword")

        board = mongo.db.board
        # data = board.find_one({"_id": ObjectId(idx)})
        data = board.find_one_and_update({"_id": ObjectId(idx)}, {
            "$inc": {"view": 1}}, return_document=True)

        if data is not None:
            result = {
                "id": data.get("_id"),
                "name": data.get("name"),
                "title": data.get("title"),
                "contents": data.get("contents"),
                "pubdate": data.get("pubdate"),
                "view": data.get("view"),
                "writer_id": data.get("writer_id", ""),
                "attachfile": data.get("attachfile", "")
            }

            comment = mongo.db.comment
            comments = comment.find({"root.idx": str(data.get("_id"))})
            return render_template("view.html", result=result, comments=list(comments), page=page, search=search, keyword=keyword, title="글 상세보기")
    return abort(404)


@bluerprint.route("/write", methods=["GET", "POST"])
def board_write():
    if request.method == "POST":
        filename = None
        if "attachfile" in request.files:
            file = request.files["attachfile"]
            if file and allowed_file(file.filename):
                filename = check_filename(file.filename)
                file.save(os.path.join(app.config['BOARD_ATTACH_FILE_PATH'], filename))

        name = request.form.get("name")
        title = request.form.get("title")
        contents = request.form.get("contents")

        request.files
        current_utc_time = round(datetime.utcnow().timestamp() * 1000)
        board = mongo.db.board
        post = {
            "name": name,
            "title": title,
            "contents": contents,
            "pubdate": current_utc_time,
            "writer_id": session.get("id"),
            "view": 0,
        }
       
        if filename is not None:
            post["attachfile"] = filename

        x = board.insert_one(post)
        print(x.inserted_id)
        return redirect(url_for("board.board_view", idx=x.inserted_id))
    else:
        return render_template("write.html", title="글 작성")


@bluerprint.route("/edit/<idx>", methods=["GET", "POST"])
def board_edit(idx):
    if request.method == "GET":
        board = mongo.db.board
        data = board.find_one({"_id": ObjectId(idx)})
        if data is None:
            flash("해당 게시물이 존재하지 않습니다.")
            return redirect(url_for("board.lists"))
        else:
            if session.get("id") == data.get("writer_id"):
                return render_template("edit.html", data=data, title="글 수정")
            else:
                flash("글 수정 권한이 없습니다.")
                return redirect(url_for("board.lists"))
    else:
        title = request.form.get("title")
        contains = request.form.get("contents")
        deleteoldfile = request.form.get("deleteoldfile", "")

        board = mongo.db.board
        data = board.find_one({"_id": ObjectId(idx)})
        if session.get("id") == data.get("writer_id"):
            filename = None
            if "attachfile" in request.files:
                file = request.files["attachfile"]
                if file and allowed_file(file.filename):
                    filename = check_filename(file.filename)
                    file.save(os.path.join(app.config["BOARD_ATTACH_FILE_PATH"], filename))

                    if data.get("attachfile"):
                        board_delete_attach_file(data.get("attachfile"))
                else:
                    if deleteoldfile == "on":
                        filename = None
                        if data.get("attachfile"):
                            board_delete_attach_file(data.get("attachfile"))
                    else:
                        filename = data.get("attachfile")

            board.update_one({"_id": ObjectId(idx)}, {
                "$set": {
                    "title": title,
                    "contents": contains,
                    "attachfile": filename
                }
            })
            flash("수정 되었습니다.")
            return redirect(url_for("board.board_view", idx=idx))
        else:
            flash("글 수정 권한이 없습니다.")
            return redirect(url_for("board.lists"))


@bluerprint.route("/delete/<idx>")
def board_delete(idx):
    board = mongo.db.board
    data = board.find_one({"_id": ObjectId(idx)})
    if data.get("writer_id") == session.get("id"):
        board.delete_one({"_id": ObjectId(idx)})
        flash("삭제 되었습니다.")
    else:
        flash("삭제 권한이 없습니다.")
    return redirect(url_for("board.lists"))
 
 
 
------------------------------------------------------------------------------------
 
 
 
-view.html-
 
 
 
{% extends "main.html" %}

{% block contents %}

<div style="padding: 50px 50px 50px 50px;">
    <table class="table table-bordered">
        <tbody>
            <tr>
                <td colspan="2">{{result.title}}</td>
            </tr>
            <tr>
                <td>{{result.name}}</td>
                <td class="text-right">{{result.pubdate|formatdatetime}}</td>
            </tr>
            {% if result.attachfile %}
            <tr>
                <td>첨부파일</td>
                <td><a href="{{url_for('board.board_files', filename=result.attachfile)}}">{{result.attachfile}}</a></td>
            </tr>
            {% endif %}
            <tr>
                <td colspan="2"><div style="min-height: 200px;"></div>{% autoescape false %}{{result.contents}}{% endautoescape %}</td>
            </tr>
        </tbody>
    </table>

    <a class="btn btn-primary" href="{{url_for('board.lists', page=page, search=search, keyword=keyword)}}">리스트</a>

    {% if session["id"] == result.writer_id %}
    <a class="btn btn-danger float-right ml-2" href="{{url_for('board.board_delete', idx=result.id)}}">글삭제</a>
    <a class="btn btn-warning float-right" href="{{url_for('board.board_edit', idx=result.id)}}">글수정</a>
    {% endif %}
    <br>
    <br>
    <form id="commentForm" name="commentForm" action="{{url_for('board.comment_write')}}" method="POST">
        <input type="hidden" name="csrf_token" value="{{csrf_token()}}">
        <input type="hidden" name="root_idx" value="{{result.id}}">
        <div>
            <span><strong>댓글</strong></span> <span id="cCnt"></span>
            <table class="table">
                <tr>
                    <td><textarea rows="3" cols="110" id="comment" name="comment" placeholder="댓글을 입력하세요"></textarea></td>
                    <td><input type="submit" class="btn btn-success" style="height: 80px;" value="등록하기"></td>
                </tr>
            </table>
        </div>
    </form>

    {% if comments|length > 0 %}
        {% for c in comments %}
            <div>
                <table class="table">
                    <tr>
                        <td width="100"><h6>{{c.name}}</h6></td>
                        <td>{{c_comment}}</td>
                        <td class="text-right" width="200">{{c.pubdate | formatdatetime}}</td>
                    </tr>
                </table>
            </div>
        {% endfor %}
    {% endif %}
</div>

{% endblock %}
 
 
 
 
 
 
 
 
------------------------------------------------------------------------------------
 
 
 
 
 
 
 
추가 궁금사항
 
 
- 글작성후에 글내용이 하단부에 붙는 이유는 무엇때문일까요?
 
 
 
 
 

 

comments = comment.find({"root.idx": str(data.get("_id"))})
print(comments)

먼저 파이썬 코드에서 위 코드처럼 comments 에 데이터가 정상적으로 확인해보시고,

 

{{comments|length}}
{{comments}}
{% if comments|length > 0 %}
        {% for c in comments %}

 

HTML 에서도 위의 코드처럼 comments 의 갯수나 comments 내용을 화면에 출력해서 어떻게 나오는지 부터 확인해봐야 할듯 합니다. 이렇게 출력을 해보고 어느쪽에서 문제가 생기는건지 부터 알아야 어떻게 수정을 할지를 판단할 수 있습니다.

 

그리고 내용이 하단에 붙는이유는..

<td colspan="2"><div style="min-height: 200px;"></div>{% autoescape false %}{{result.contents}}{% endautoescape %}</td>

 

위 코드를 보면 <div> 태그가 열리고 바로 </div> 닫혀 있습니다. div 태그 사이에 내용이 출력되어야 합니다.

 

문신호님의 프로필

문신호

질문자

2022.04.25

계속 물어봐서 죄송합니다~!

 

내용 확인 했는데... 댓글하단부에 빈 배열같은게 생긴거 같은데.. db내용은 들어가지가 않네요..!

 

글작성 에디터에서 하단부로 내려가는거는 지적해주셔서 수정하니까 정상작동 됩니다~!

 

-이미지 -

 

 

 

- TERMINAL 내용-

 

* Detected change in 'c:\\Users\\sosgx\\Desktop\\myweb\\main\\board.py', reloading

 * Restarting with stat

 * Debugger is active!

 * Debugger PIN: 740-813-285

 * Running on all addresses.

   WARNING: This is a development server. Do not use it in a production deployment.

 * Running on http://192.168.0.34:9000/ (Press CTRL+C to quit)

 * Detected change in 'c:\\Users\\sosgx\\Desktop\\myweb\\main\\board.py', reloading

 * Restarting with stat

 * Debugger is active!

 * Debugger PIN: 740-813-285

 * Running on all addresses.

   WARNING: This is a development server. Do not use it in a production deployment.

 * Running on http://192.168.0.34:9000/ (Press CTRL+C to quit)

<pymongo.cursor.Cursor object at 0x000002A11A820358>

127.0.0.1 - - [25/Apr/2022 15:33:48] "GET /board/view/62663e6799e4b938f26b8aa0?page=1&search=-1&keyword= HTTP/1.1" 200 - * Detected change in 'c:\\Users\\sosgx\\Desktop\\myweb\\main\\board.py', reloading

 * Restarting with stat

 * Debugger is active!

 * Debugger PIN: 740-813-285

 * Running on all addresses.

   WARNING: This is a development server. Do not use it in a production deployment.

 * Running on http://192.168.0.34:9000/ (Press CTRL+C to quit)

{}

127.0.0.1 - - [25/Apr/2022 15:36:36] "GET /board/list HTTP/1.1" 200 -

127.0.0.1 - - [25/Apr/2022 15:36:37] "GET /board/write HTTP/1.1" 200 -

6266418622c4953c23dcdb14

127.0.0.1 - - [25/Apr/2022 15:36:54] "POST /board/write HTTP/1.1" 302 -

<pymongo.cursor.Cursor object at 0x0000021C73C67550>

127.0.0.1 - - [25/Apr/2022 15:36:54] "GET /board/view/6266418622c4953c23dcdb14 HTTP/1.1" 200 -

POST OK

{'root_idx': '6266418622c4953c23dcdb14', 'writer_id': '6265f517edcd20c927a2e8c1', 'name': '홍길동', 'comment': '정말 재

미있나요?', 'pubdate': 1650836227229}

127.0.0.1 - - [25/Apr/2022 15:37:07] "POST /board/comment_write HTTP/1.1" 302 -

<pymongo.cursor.Cursor object at 0x0000021C7455FA58>

127.0.0.1 - - [25/Apr/2022 15:37:07] "GET /board/view/6266418622c4953c23dcdb14 HTTP/1.1" 200 -

 

 

 

 

- 코드 -

-board.py-

 

from main import *
from flask import Blueprint, send_from_directory
from flask import send_from_directory

bluerprint = Blueprint("board", __name__, url_prefix="/board")

def board_delete_attach_file(filename):
    abs_path = os.path.join(app.config["BOARD_ATTACH_FILE_PATH"], filename)
    if os.path.exists(abs_path):
        os.remove(abs_path)
        return True
    return False


@bluerprint.route("/comment_write", methods=["POST"])
@login_required
def comment_write():
    if request.method == "POST":
        print("POST OK")
        name = session.get("name")
        writer_id = session.get("id")
        root_idx = request.form.get("root_idx")
        comment = request.form.get("comment")
        current_utc_time = round(datetime.utcnow().timestamp() * 1000)
        c_comment = mongo.db.comment
        post = {
            "root_idx": str(root_idx),
            "writer_id": writer_id,
            "name": name,
            "comment": comment,
            "pubdate": current_utc_time
        }

        print(post)

        c_comment.insert_one(post)
        return redirect(url_for("board.board_view", idx=root_idx))
   

@bluerprint.route("/upload_image", methods=["POST"])
def upload_image():
    if request.method == "POST":
        file = request.files["image"]
        if file and allowed_file(file.filename):
            filename = "{}.jpg".format(rand_generator())
            savefilepath = os.path.join(app.config["BOARD_IMAGE_PATH"], filename)
            file.save(savefilepath)
            return url_for("board.board_images", filename=filename)


@bluerprint.route("/images/<filename>")
def board_images(filename):
    return send_from_directory(app.config["BOARD_IMAGE_PATH"], filename)


@bluerprint.route("/files/<filename>")
def board_files(filename):
    return send_from_directory(app.config["BOARD_ATTACH_FILE_PATH"], filename, as_attachment=True)


@bluerprint.route("/list")
def lists():
    # 페이지 값 (값이 없는 경우 기본값는 1)
    page = request.args.get("page", 1, type=int)
    # 한페이지당 몇개의 게시물을 출력할지
    limit = request.args.get("limit", 5, type=int)

    search = request.args.get("search", -1, type=int)
    keyword = request.args.get("keyword", "", type=str)

    # 최종적으로 완성된 쿼리를 만들 변수
    query = {}
    # 검색어 상태를 추가할 리스트 변수
    search_list = []

    if search == 0:
        search_list.append({"title": {"$regex": keyword}})
    elif search == 1:
        search_list.append({"contents": {"$regex": keyword}})
    elif search == 2:
        search_list.append({"title": {"$regex": keyword}})
        search_list.append({"contents": {"$regex": keyword}})
    elif search == 3:
        search_list.append({"name": {"$regex": keyword}})

    # 검색 대상이 한개라도 존재할 경우 query 변수에 $or 리스트를 쿼리 합니다.
    if len(search_list) > 0:
        query = {"$or": search_list}

    print(query)

    board = mongo.db.board
    datas = board.find({}).skip(
        (page - 1) * limit).limit(limit).sort("pubdate", -1)

    # 게시물의 총 갯수
    tot_count = board.count_documents({})
    # 마지막 페이지의 수를 구한다.
    last_page_num = math.ceil(tot_count / limit)
    # 페이지 블럭을 5개씩 표기
    block_size = 5
    # 현재 블럭의 위치
    block_num = int((page - 1) / block_size)
    # 블럭의 시작 위치
    block_start = int((block_size * block_num) + 1)
    # 블럭의 끝 위치
    block_last = math.ceil(block_start + (block_size - 1))

    return render_template(
        "list.html",
        datas=list(datas),
        limit=limit,
        page=page,
        block_start=block_start,
        block_last=block_last,
        last_page_num=last_page_num,
        search=search,
        keyword=keyword,
        title="게시판 리스트")


@bluerprint.route("/view/<idx>")
@login_required
def board_view(idx):
    # idx = request.args.get("idx")
    if idx is not None:
        page = request.args.get("page")
        search = request.args.get("search")
        keyword = request.args.get("keyword")

        board = mongo.db.board
        # data = board.find_one({"_id": ObjectId(idx)})
        data = board.find_one_and_update({"_id": ObjectId(idx)}, {
            "$inc": {"view": 1}}, return_document=True)

        if data is not None:
            result = {
                "id": data.get("_id"),
                "name": data.get("name"),
                "title": data.get("title"),
                "contents": data.get("contents"),
                "pubdate": data.get("pubdate"),
                "view": data.get("view"),
                "writer_id": data.get("writer_id", ""),
                "attachfile": data.get("attachfile", "")
            }

            comment = mongo.db.comment
            comments = comment.find({"root.idx": str(data.get("_id"))})
            print(comments)
            return render_template("view.html", result=result, comments=list(comments), page=page, search=search, keyword=keyword, title="글 상세보기")
    return abort(404)


@bluerprint.route("/write", methods=["GET", "POST"])
def board_write():
    if request.method == "POST":
        filename = None
        if "attachfile" in request.files:
            file = request.files["attachfile"]
            if file and allowed_file(file.filename):
                filename = check_filename(file.filename)
                file.save(os.path.join(app.config['BOARD_ATTACH_FILE_PATH'], filename))

        name = request.form.get("name")
        title = request.form.get("title")
        contents = request.form.get("contents")

        request.files
        current_utc_time = round(datetime.utcnow().timestamp() * 1000)
        board = mongo.db.board
        post = {
            "name": name,
            "title": title,
            "contents": contents,
            "pubdate": current_utc_time,
            "writer_id": session.get("id"),
            "view": 0,
        }
       
        if filename is not None:
            post["attachfile"] = filename

        x = board.insert_one(post)
        print(x.inserted_id)
        return redirect(url_for("board.board_view", idx=x.inserted_id))
    else:
        return render_template("write.html", title="글 작성")


@bluerprint.route("/edit/<idx>", methods=["GET", "POST"])
def board_edit(idx):
    if request.method == "GET":
        board = mongo.db.board
        data = board.find_one({"_id": ObjectId(idx)})
        if data is None:
            flash("해당 게시물이 존재하지 않습니다.")
            return redirect(url_for("board.lists"))
        else:
            if session.get("id") == data.get("writer_id"):
                return render_template("edit.html", data=data, title="글 수정")
            else:
                flash("글 수정 권한이 없습니다.")
                return redirect(url_for("board.lists"))
    else:
        title = request.form.get("title")
        contains = request.form.get("contents")
        deleteoldfile = request.form.get("deleteoldfile", "")

        board = mongo.db.board
        data = board.find_one({"_id": ObjectId(idx)})
        if session.get("id") == data.get("writer_id"):
            filename = None
            if "attachfile" in request.files:
                file = request.files["attachfile"]
                if file and allowed_file(file.filename):
                    filename = check_filename(file.filename)
                    file.save(os.path.join(app.config["BOARD_ATTACH_FILE_PATH"], filename))

                    if data.get("attachfile"):
                        board_delete_attach_file(data.get("attachfile"))
                else:
                    if deleteoldfile == "on":
                        filename = None
                        if data.get("attachfile"):
                            board_delete_attach_file(data.get("attachfile"))
                    else:
                        filename = data.get("attachfile")

            board.update_one({"_id": ObjectId(idx)}, {
                "$set": {
                    "title": title,
                    "contents": contains,
                    "attachfile": filename
                }
            })
            flash("수정 되었습니다.")
            return redirect(url_for("board.board_view", idx=idx))
        else:
            flash("글 수정 권한이 없습니다.")
            return redirect(url_for("board.lists"))


@bluerprint.route("/delete/<idx>")
def board_delete(idx):
    board = mongo.db.board
    data = board.find_one({"_id": ObjectId(idx)})
    if data.get("writer_id") == session.get("id"):
        board.delete_one({"_id": ObjectId(idx)})
        flash("삭제 되었습니다.")
    else:
        flash("삭제 권한이 없습니다.")
    return redirect(url_for("board.lists"))
 
 
---------------------------------------------------------------------------------
 
 
 
-viwe.html-
 
 
 
{% extends "main.html" %}

{% block contents %}

<div style="padding: 50px 50px 50px 50px;">
    <table class="table table-bordered">
        <tbody>
            <tr>
                <td colspan="2">{{result.title}}</td>
            </tr>
            <tr>
                <td>{{result.name}}</td>
                <td class="text-right">{{result.pubdate|formatdatetime}}</td>
            </tr>
            {% if result.attachfile %}
            <tr>
                <td>첨부파일</td>
                <td><a href="{{url_for('board.board_files', filename=result.attachfile)}}">{{result.attachfile}}</a></td>
            </tr>
            {% endif %}
            <tr>
                <td colspan="2"><div style="min-height: 200px;">{% autoescape false %}{{result.contents}}{% endautoescape %}</div></td>
            </tr>
        </tbody>
    </table>

    <a class="btn btn-primary" href="{{url_for('board.lists', page=page, search=search, keyword=keyword)}}">리스트</a>

    {% if session["id"] == result.writer_id %}
    <a class="btn btn-danger float-right ml-2" href="{{url_for('board.board_delete', idx=result.id)}}">글삭제</a>
    <a class="btn btn-warning float-right" href="{{url_for('board.board_edit', idx=result.id)}}">글수정</a>
    {% endif %}
    <br>
    <br>
    <form id="commentForm" name="commentForm" action="{{url_for('board.comment_write')}}" method="POST">
        <input type="hidden" name="csrf_token" value="{{csrf_token()}}">
        <input type="hidden" name="root_idx" value="{{result.id}}">
        <div>
            <span><strong>댓글</strong></span> <span id="cCnt"></span>
            <table class="table">
                <tr>
                    <td><textarea rows="3" cols="110" id="comment" name="comment" placeholder="댓글을 입력하세요"></textarea></td>
                    <td><input type="submit" class="btn btn-success" style="height: 80px;" value="등록하기"></td>
                </tr>
            </table>
        </div>
    </form>

    {{comments|length}}
    {{comments}}
    {% if comments|length > 0 %}
        {% for c in comments %}
            <div>
                <table class="table">
                    <tr>
                        <td width="100"><h6>{{c.name}}</h6></td>
                        <td>{{c_comment}}</td>
                        <td class="text-right" width="200">{{c.pubdate | formatdatetime}}</td>
                    </tr>
                </table>
            </div>
        {% endfor %}
    {% endif %}
</div>

{% endblock %}

지금 상황을 보면서 이해를 하실 필요가 있습니다.

 

위 이미지를 보면 플라스크 로그에는 일단 view 요청시 comments 가 print() 되는걸 확인할 수 있는데,

실제 HTML 쪽에서는 comments 의 갯수가 0으로 출력되는 상황입니다.

comment = mongo.db.comment
comments = comment.find({"root.idx": str(data.get("_id"))})
com_result = []
for c in comments:
    com_result.append(c)
print(com_result)
return render_template("view.html", result=result, coms=com_result, page=page, search=search, keyword=keyword, title="글 상세보기")

 

일단 위의 코드처럼 comments 의 내용을 빈 리스트를 하나 만들어서 append 시키고 comments 변수명을 한번 변경해보는것도 좋을듯 합니다. 위 코드에서는 coms 라는 변수명으로 HTML에 데이터를 넘기게 수정되었습니다.

    {{coms|length}}
    {{coms}}
    {% if coms|length > 0 %}
        {% for c in coms%}
            <div>
                <table class="table">
                    <tr>
                        <td width="100"><h6>{{c.name}}h6>td>
                        <td>{{c_comment}}td>
                        <td class="text-right" width="200">{{c.pubdate | formatdatetime}}td>
                    tr>
                table>
            div>
        {% endfor %}
    {% endif %}

 

HTML 쪽에도 위의 코드처럼 변수명을 coms 로 변경해서 한번 처리해보시기 바랍니다. 그리고 지금 이 코드의 내용을 이해하셔야 합니다. 그래야 강좌의 진도를 맞춰가실 수 있습니다.

문신호님의 프로필

문신호

질문자

2022.04.25

답변 감사합니다.

계속 물어봐서 죄송합니다..!

 

그래도 해결은 해야할꺼 같아서.. 계속 물어봅니다..ㅠ

 

아이디가 다를경우는 아까와 같은 값으로 나오는데요..!

 

-이미지1-

 

알려주신 방식으로 다시 수정을 해보았습니다.

아래 root.idx로 알려주셔서 해 보니까 값이 안떠서 root_idx로 변경하니까 값은 들어오는데.. 여러개가 들어오니까 알수가 없네요..!

 

comment = mongo.db.comment
        comments = comment.find({"root_idx": str(data.get("_id"))})
        com_result = []
        for c in comments:
            com_result.append(c)
        print(com_result)
        return render_template("view.html", result=result, coms=com_result, page=page, search=search, keyword=keyword, title="글 상세보기")

 

 

-이미지2-

 

 

-TERMINAL-

127.0.0.1 - - [25/Apr/2022 17:07:40] "GET /board/list?page=1&search=-1&keyword= HTTP/1.1" 200 -

[{'_id': ObjectId('62662add897b71850a8b5877'), 'root_idx': '6262640e3ebba41f4b4f904b', 'writer_id': '6265f517edcd20c927a2e8c1', 'name': '홍길동', 'comment': '우왕!', 'pubdate': 1650830413380}, {'_id': ObjectId('62662ccb897b71850a8b587a'), 'root_idx': '6262640e3ebba41f4b4f904b', 'writer_id': '6265f517edcd20c927a2e8c1', 'name': '홍길동', 'comment': '남박사!', 

'pubdate': 1650830907794}, {'_id': ObjectId('62662d28897b71850a8b587b'), 'root_idx': '6262640e3ebba41f4b4f904b', 'writer_id': '6265f517edcd20c927a2e8c1', 'name': '홍길동', 'comment': 'ㅁㄴㅇㅁㄴㅇ', 'pubdate': 1650831000605}, {'_id': ObjectId('62663e3b99e4b938f26b8a9f'), 'root_idx': '6262640e3ebba41f4b4f904b', 'writer_id': '6265f517edcd20c927a2e8c1', 'name': '홍길동', 'comment': '길동이!', 'pubdate': 1650835371866}]

 

 

- 코드 -

 

-board.py-

 

from main import *
from flask import Blueprint, send_from_directory
from flask import send_from_directory

bluerprint = Blueprint("board", __name__, url_prefix="/board")

def board_delete_attach_file(filename):
    abs_path = os.path.join(app.config["BOARD_ATTACH_FILE_PATH"], filename)
    if os.path.exists(abs_path):
        os.remove(abs_path)
        return True
    return False


@bluerprint.route("/comment_write", methods=["POST"])
@login_required
def comment_write():
    if request.method == "POST":
        print("POST OK")
        name = session.get("name")
        writer_id = session.get("id")
        root_idx = request.form.get("root_idx")
        comment = request.form.get("comment")
        current_utc_time = round(datetime.utcnow().timestamp() * 1000)
        c_comment = mongo.db.comment
        post = {
            "root_idx": str(root_idx),
            "writer_id": writer_id,
            "name": name,
            "comment": comment,
            "pubdate": current_utc_time
        }

        print(post)

        c_comment.insert_one(post)
        return redirect(url_for("board.board_view", idx=root_idx))
   

@bluerprint.route("/upload_image", methods=["POST"])
def upload_image():
    if request.method == "POST":
        file = request.files["image"]
        if file and allowed_file(file.filename):
            filename = "{}.jpg".format(rand_generator())
            savefilepath = os.path.join(app.config["BOARD_IMAGE_PATH"], filename)
            file.save(savefilepath)
            return url_for("board.board_images", filename=filename)


@bluerprint.route("/images/<filename>")
def board_images(filename):
    return send_from_directory(app.config["BOARD_IMAGE_PATH"], filename)


@bluerprint.route("/files/<filename>")
def board_files(filename):
    return send_from_directory(app.config["BOARD_ATTACH_FILE_PATH"], filename, as_attachment=True)


@bluerprint.route("/list")
def lists():
    # 페이지 값 (값이 없는 경우 기본값는 1)
    page = request.args.get("page", 1, type=int)
    # 한페이지당 몇개의 게시물을 출력할지
    limit = request.args.get("limit", 5, type=int)

    search = request.args.get("search", -1, type=int)
    keyword = request.args.get("keyword", "", type=str)

    # 최종적으로 완성된 쿼리를 만들 변수
    query = {}
    # 검색어 상태를 추가할 리스트 변수
    search_list = []

    if search == 0:
        search_list.append({"title": {"$regex": keyword}})
    elif search == 1:
        search_list.append({"contents": {"$regex": keyword}})
    elif search == 2:
        search_list.append({"title": {"$regex": keyword}})
        search_list.append({"contents": {"$regex": keyword}})
    elif search == 3:
        search_list.append({"name": {"$regex": keyword}})

    # 검색 대상이 한개라도 존재할 경우 query 변수에 $or 리스트를 쿼리 합니다.
    if len(search_list) > 0:
        query = {"$or": search_list}

    print(query)

    board = mongo.db.board
    datas = board.find({}).skip(
        (page - 1) * limit).limit(limit).sort("pubdate", -1)

    # 게시물의 총 갯수
    tot_count = board.count_documents({})
    # 마지막 페이지의 수를 구한다.
    last_page_num = math.ceil(tot_count / limit)
    # 페이지 블럭을 5개씩 표기
    block_size = 5
    # 현재 블럭의 위치
    block_num = int((page - 1) / block_size)
    # 블럭의 시작 위치
    block_start = int((block_size * block_num) + 1)
    # 블럭의 끝 위치
    block_last = math.ceil(block_start + (block_size - 1))

    return render_template(
        "list.html",
        datas=list(datas),
        limit=limit,
        page=page,
        block_start=block_start,
        block_last=block_last,
        last_page_num=last_page_num,
        search=search,
        keyword=keyword,
        title="게시판 리스트")


@bluerprint.route("/view/<idx>")
@login_required
def board_view(idx):
    # idx = request.args.get("idx")
    if idx is not None:
        page = request.args.get("page")
        search = request.args.get("search")
        keyword = request.args.get("keyword")

        board = mongo.db.board
        # data = board.find_one({"_id": ObjectId(idx)})
        data = board.find_one_and_update({"_id": ObjectId(idx)}, {
            "$inc": {"view": 1}}, return_document=True)

        if data is not None:
            result = {
                "id": data.get("_id"),
                "name": data.get("name"),
                "title": data.get("title"),
                "contents": data.get("contents"),
                "pubdate": data.get("pubdate"),
                "view": data.get("view"),
                "writer_id": data.get("writer_id", ""),
                "attachfile": data.get("attachfile", "")
            }

        comment = mongo.db.comment
        comments = comment.find({"root_idx": str(data.get("_id"))})
        com_result = []
        for c in comments:
            com_result.append(c)
        print(com_result)
        return render_template("view.html", result=result, coms=com_result, page=page, search=search, keyword=keyword, title="글 상세보기")
    return abort(404)


@bluerprint.route("/write", methods=["GET", "POST"])
def board_write():
    if request.method == "POST":
        filename = None
        if "attachfile" in request.files:
            file = request.files["attachfile"]
            if file and allowed_file(file.filename):
                filename = check_filename(file.filename)
                file.save(os.path.join(app.config['BOARD_ATTACH_FILE_PATH'], filename))

        name = request.form.get("name")
        title = request.form.get("title")
        contents = request.form.get("contents")

        request.files
        current_utc_time = round(datetime.utcnow().timestamp() * 1000)
        board = mongo.db.board
        post = {
            "name": name,
            "title": title,
            "contents": contents,
            "pubdate": current_utc_time,
            "writer_id": session.get("id"),
            "view": 0,
        }
       
        if filename is not None:
            post["attachfile"] = filename

        x = board.insert_one(post)
        print(x.inserted_id)
        return redirect(url_for("board.board_view", idx=x.inserted_id))
    else:
        return render_template("write.html", title="글 작성")


@bluerprint.route("/edit/<idx>", methods=["GET", "POST"])
def board_edit(idx):
    if request.method == "GET":
        board = mongo.db.board
        data = board.find_one({"_id": ObjectId(idx)})
        if data is None:
            flash("해당 게시물이 존재하지 않습니다.")
            return redirect(url_for("board.lists"))
        else:
            if session.get("id") == data.get("writer_id"):
                return render_template("edit.html", data=data, title="글 수정")
            else:
                flash("글 수정 권한이 없습니다.")
                return redirect(url_for("board.lists"))
    else:
        title = request.form.get("title")
        contains = request.form.get("contents")
        deleteoldfile = request.form.get("deleteoldfile", "")

        board = mongo.db.board
        data = board.find_one({"_id": ObjectId(idx)})
        if session.get("id") == data.get("writer_id"):
            filename = None
            if "attachfile" in request.files:
                file = request.files["attachfile"]
                if file and allowed_file(file.filename):
                    filename = check_filename(file.filename)
                    file.save(os.path.join(app.config["BOARD_ATTACH_FILE_PATH"], filename))

                    if data.get("attachfile"):
                        board_delete_attach_file(data.get("attachfile"))
                else:
                    if deleteoldfile == "on":
                        filename = None
                        if data.get("attachfile"):
                            board_delete_attach_file(data.get("attachfile"))
                    else:
                        filename = data.get("attachfile")

            board.update_one({"_id": ObjectId(idx)}, {
                "$set": {
                    "title": title,
                    "contents": contains,
                    "attachfile": filename
                }
            })
            flash("수정 되었습니다.")
            return redirect(url_for("board.board_view", idx=idx))
        else:
            flash("글 수정 권한이 없습니다.")
            return redirect(url_for("board.lists"))


@bluerprint.route("/delete/<idx>")
def board_delete(idx):
    board = mongo.db.board
    data = board.find_one({"_id": ObjectId(idx)})
    if data.get("writer_id") == session.get("id"):
        board.delete_one({"_id": ObjectId(idx)})
        flash("삭제 되었습니다.")
    else:
        flash("삭제 권한이 없습니다.")
    return redirect(url_for("board.lists"))
 
-----------------------------------------------------------------------------------
 
 
-view.html-
 
 
{% extends "main.html" %}

{% block contents %}

<div style="padding: 50px 50px 50px 50px;">
    <table class="table table-bordered">
        <tbody>
            <tr>
                <td colspan="2">{{result.title}}</td>
            </tr>
            <tr>
                <td>{{result.name}}</td>
                <td class="text-right">{{result.pubdate|formatdatetime}}</td>
            </tr>
            {% if result.attachfile %}
            <tr>
                <td>첨부파일</td>
                <td><a href="{{url_for('board.board_files', filename=result.attachfile)}}">{{result.attachfile}}</a></td>
            </tr>
            {% endif %}
            <tr>
                <td colspan="2"><div style="min-height: 200px;">{% autoescape false %}{{result.contents}}{% endautoescape %}</div></td>
            </tr>
        </tbody>
    </table>

    <a class="btn btn-primary" href="{{url_for('board.lists', page=page, search=search, keyword=keyword)}}">리스트</a>

    {% if session["id"] == result.writer_id %}
    <a class="btn btn-danger float-right ml-2" href="{{url_for('board.board_delete', idx=result.id)}}">글삭제</a>
    <a class="btn btn-warning float-right" href="{{url_for('board.board_edit', idx=result.id)}}">글수정</a>
    {% endif %}
    <br>
    <br>
    <form id="commentForm" name="commentForm" action="{{url_for('board.comment_write')}}" method="POST">
        <input type="hidden" name="csrf_token" value="{{csrf_token()}}">
        <input type="hidden" name="root_idx" value="{{result.id}}">
        <div>
            <span><strong>댓글</strong></span> <span id="cCnt"></span>
            <table class="table">
                <tr>
                    <td><textarea rows="3" cols="110" id="comment" name="comment" placeholder="댓글을 입력하세요"></textarea></td>
                    <td><input type="submit" class="btn btn-success" style="height: 80px;" value="등록하기"></td>
                </tr>
            </table>
        </div>
    </form>

    {{coms|length}}
    {{coms}}
    {% if coms|length > 0 %}
        {% for c in coms%}
            <div>
                <table class="table">
                    <tr>
                        <td width="100"><h6>{{c.name}}h6>td>
                        <td>{{c_comment}}td>
                        <td class="text-right" width="200">{{c.pubdate | formatdatetime}}td>
                    tr>
                table>
            div>
        {% endfor %}
    {% endif %}
</div>

{% endblock %}
 

 

음..... 

문제가 root_idx 를 root.idx 로 사용해서 생긴 문제였군요....

일단 질문을 자주 주시는건 아무 상관없습니다. 다만 현재 코드에 대한 이해를 조금 더 하셔야할 필요는 있어 보입니다. 물론 초보입장에서는 쉬운 문제가 아니지만 그래도 이해해보시는데 좀 더 시간을 투자해보시는것도 좋아보입니다.

comments = comment.find({"root_idx": str(data.get("_id"))})

지금까지의 문제는 위의 코드처럼 find 대상이 root_idx 인 요소의 이름이여야 하는데 root.idx 는 존재하지 않는 키 이름을 접근하는게 문제였던걸로 보입니다.

{{coms|length}}
{{coms}}

위코드는 오류를 찾기 위한 임시로 작성한 코드이므로 당연히 제거해야 하는 코드 입니다.

문신호님의 프로필

문신호

질문자

2022.04.25

남박사님 답변 주셔서 감사합니다.

맨처음으로 돌아와서 다시 잘보고 수정하였는데요..!

{% if comments|length > 0 %} 이부분과
comments=list(comments) 이부분 수정하고 나머지 오타 나는 부분 찾아서 수정하니까 문제는 해결했습니다..!
 
 
답답변 주셔서 감사합니다! 좀더 이해 해보도록 노력해야겠네요!

네 해결하셨다니 다행입니다.

결과를 보는것도 중요하고 과정을 이해하는것도 중요하고.. 참 어렵습니다만.. 그래도 포기하지 않는게 가장 중요합니다. 궁금하신 사항은 뭐든 항상 질문주세요.

0

네 말씀하신대로 몽고디비 상위 버전에서는 더이상 커서에 count() 함수를 지원하지 않고 count_documents() 로 대체 되었습니다. 그런데 지금 코드에서는 갯수만 필요한게 아니라 커서를 갖고 데이터를 구하는 내용도 있기 때문에 단순히 count_documents() 함수로 처리하기가 좀 애매한 부분이 있습니다. 갯수와 데이터를 모두 사용해야 하기 때문에 일단,

 

 {% if comments.count() > 0 %}

위 코드 부분을,

{% if len(list(cursor.clone())) > 0 %}

위와 같이 한번 수정해보시기 바랍니다.

 

문신호님의 프로필

문신호

질문자

2022.04.24

안녕하세요 남박사님

 

말씀해주신대로 적용해보니까.  cursor 찾을수 없다고 나오는데요.. cursor를 별도로 지정해주지 않아서 그런지 모르겠지만.. 오류가 다시 납니다..ㅠ

 

 

jinja2.exceptions.UndefinedError

jinja2.exceptions.UndefinedError: 'cursor' is undefined

Traceback (most recent call last)

 

  • File "C:\Users\sosgx\AppData\Roaming\Python\Python36\site-packages\flask\app.py", line 2091, in __call__

        def __call__(self, environ: dict, start_response: t.Callable) -> t.Any:
            """The WSGI server calls the Flask application object as the
            WSGI application. This calls :meth:`wsgi_app`, which can be
            wrapped to apply middleware.
            """
            return self.wsgi_app(environ, start_response)
  • File "C:\Users\sosgx\AppData\Roaming\Python\Python36\site-packages\flask\app.py", line 2076, in wsgi_app

    response = self.handle_exception(e)
  • File "C:\Users\sosgx\AppData\Roaming\Python\Python36\site-packages\flask\app.py", line 2073, in wsgi_app

    response = self.full_dispatch_request()
  • File "C:\Users\sosgx\AppData\Roaming\Python\Python36\site-packages\flask\app.py", line 1518, in full_dispatch_request

    rv = self.handle_user_exception(e)
  • File "C:\Users\sosgx\AppData\Roaming\Python\Python36\site-packages\flask\app.py", line 1516, in full_dispatch_request

            self.try_trigger_before_first_request_functions()
            try:
                request_started.send(self)
                rv = self.preprocess_request()
                if rv is None:
                    rv = self.dispatch_request()
            except Exception as e:
                rv = self.handle_user_exception(e)
            return self.finalize_request(rv)
     
        def finalize_request(
  • File "C:\Users\sosgx\AppData\Roaming\Python\Python36\site-packages\flask\app.py", line 1502, in dispatch_request

    return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
  • File "c:\Users\sosgx\Desktop\myweb\main\common.py", line 41, in decorated_function

    return f(*args, **kwargs)
  • File "c:\Users\sosgx\Desktop\myweb\main\board.py", line 150, in board_view

    return render_template("view.html", result=result, comments=comments, page=page, search=search, keyword=keyword, title="글 상세보기")
  • File "C:\Users\sosgx\AppData\Roaming\Python\Python36\site-packages\flask\templating.py", line 150, in render_template

    ctx.app,
  • File "C:\Users\sosgx\AppData\Roaming\Python\Python36\site-packages\flask\templating.py", line 128, in _render

    rv = template.render(context)
  • File "C:\Users\sosgx\AppData\Roaming\Python\Python36\site-packages\jinja2\environment.py", line 1291, in render

    self.environment.handle_exception()
  • File "C:\Users\sosgx\AppData\Roaming\Python\Python36\site-packages\jinja2\environment.py", line 925, in handle_exception

    raise rewrite_traceback_stack(source=source)
  • File "c:\Users\sosgx\Desktop\myweb\main\templates\view.html", line 1, in top-level template code

    {% extends "main.html" %}
  • File "c:\Users\sosgx\Desktop\myweb\main\templates\main.html", line 48, in top-level template code

    {% block contents %}
  • File "c:\Users\sosgx\Desktop\myweb\main\templates\view.html", line 49, in block 'contents'

    {% if len(list(cursor.clone())) > 0 %}
  • File "C:\Users\sosgx\AppData\Roaming\Python\Python36\site-packages\jinja2\environment.py", line 474, in getattr

    return getattr(obj, attribute)
jinja2.exceptions.UndefinedError: 'cursor' is undefined
The debugger caught an exception in your WSGI application. You can now look at the traceback which led to the error.

To switch between the interactive traceback and the plaintext one, you can click on the "Traceback" headline. From the text traceback you can also create a paste of it. For code execution mouse-over the frame you want to debug and click on the console icon on the right side.

You can execute arbitrary Python code in the stack frames and there are some extra helpers available for introspection:

  • dump() shows all variables in the frame
  • dump(obj) dumps all that's known about the object

-----------------------------------------------------------------------

 

-코드-

-view.html-

 

 

{% extends "main.html" %}

{% block contents %}

<div style="padding: 50px 50px 50px 50px;">
    <table class="table table-bordered">
        <tbody>
            <tr>
                <td colspan="2">{{result.title}}</td>
            </tr>
            <tr>
                <td>{{result.name}}</td>
                <td class="text-right">{{result.pubdate|formatdatetime}}</td>
            </tr>
            {% if result.attachfile %}
            <tr>
                <td>첨부파일</td>
                <td><a href="{{url_for('board.board_files', filename=result.attachfile)}}">{{result.attachfile}}</a></td>
            </tr>
            {% endif %}
            <tr>
                <td colspan="2"><div style="min-height: 200px;"></div>{% autoescape false %}{{result.contents}}{% endautoescape %}</td>
            </tr>
        </tbody>
    </table>

    <a class="btn btn-primary" href="{{url_for('board.lists', page=page, search=search, keyword=keyword)}}">리스트</a>

    {% if session["id"] == result.writer_id %}
    <a class="btn btn-danger float-right ml-2" href="{{url_for('board.board_delete', idx=result.id)}}">글삭제</a>
    <a class="btn btn-warning float-right" href="{{url_for('board.board_edit', idx=result.id)}}">글수정</a>
    {% endif %}
    <br>
    <br>
    <form id="commentForm" name="commentForm" action="{{url_for('board.comment_write')}}" method="POST">
        <input type="hidden" name="csrf_token" value="{{csrf_token()}}">
        <input type="hidden" name="root_idx" value="{{result.id}}">
        <div>
            <span><strong>댓글</strong></span> <span id="cCnt"></span>
            <table class="table">
                <tr>
                    <td><textarea rows="3" cols="110" id="comment" name="comment" placeholder="댓글을 입력하세요"></textarea></td>
                    <td><input type="submit" class="btn btn-success" style="height: 80px;" value="등록하기"></td>
                </tr>
            </table>
        </div>
    </form>

    {% if len(list(cursor.clone())) > 0 %}
        {% for c in comments %}
            <div>
                <table class="table">
                    <tr>
                        <td width="100"><h6>{{c.name}}</h6></td>
                        <td>{{c.comment}}</td>
                        <td class="text-right" width="200">{{c.pubdate | formatdatetime}}</td>
                    </tr>
                </table>
            </div>
        {% endfor %}
    {% endif %}
</div>

{% endblock %}

 

{% if len(list(cursor.clone())) > 0 %}

# 커서객체를 이렇게 사용해야 한다는 의미였습니다.
{% if len(list(comments.clone())) > 0 %}

그대로 작성하시면 안되고.. 위 코드처럼 커서에 대한 변수명을 지정해서 해보시기 바랍니다.

 

문신호님의 프로필

문신호

질문자

2022.04.25

남박사님 답변 주셔서 감사합니다..! 말씀해주신대로 해보니까 그래도 아직 오류가 나는 부분이 있어서 다시한번 질문 드립니다.

몽고DB버전을 바꿔야 하는건가요..ㅠ?

이번에는 list를 정의할수 없다고 하는데.. 어디 부분에서 만들어서 진행을 해야할까요..?

 

-오류-

 

jinja2.exceptions.UndefinedError

jinja2.exceptions.UndefinedError: 'list' is undefined

Traceback (most recent call last)

 

  • File "C:\Users\sosgx\Desktop\myweb\venv\lib\site-packages\flask\app.py", line 2091, in __call__

    return self.wsgi_app(environ, start_response)
  • File "C:\Users\sosgx\Desktop\myweb\venv\lib\site-packages\flask\app.py", line 2076, in wsgi_app

    response = self.handle_exception(e)
  • File "C:\Users\sosgx\Desktop\myweb\venv\lib\site-packages\flask\app.py", line 2073, in wsgi_app

    response = self.full_dispatch_request()
  • File "C:\Users\sosgx\Desktop\myweb\venv\lib\site-packages\flask\app.py", line 1518, in full_dispatch_request

    rv = self.handle_user_exception(e)
  • File "C:\Users\sosgx\Desktop\myweb\venv\lib\site-packages\flask\app.py", line 1516, in full_dispatch_request

    rv = self.dispatch_request()
  • File "C:\Users\sosgx\Desktop\myweb\venv\lib\site-packages\flask\app.py", line 1502, in dispatch_request

    return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
  • File "c:\Users\sosgx\Desktop\myweb\main\common.py", line 41, in decorated_function

    return f(*args, **kwargs)
  • File "c:\Users\sosgx\Desktop\myweb\main\board.py", line 150, in board_view

    return render_template("view.html", result=result, comments=comments, page=page, search=search, keyword=keyword, title="글 상세보기")
  • File "C:\Users\sosgx\Desktop\myweb\venv\lib\site-packages\flask\templating.py", line 150, in render_template

    ctx.app,
  • File "C:\Users\sosgx\Desktop\myweb\venv\lib\site-packages\flask\templating.py", line 128, in _render

    rv = template.render(context)
  • File "C:\Users\sosgx\Desktop\myweb\venv\lib\site-packages\jinja2\environment.py", line 1291, in render

    self.environment.handle_exception()
  • File "C:\Users\sosgx\Desktop\myweb\venv\lib\site-packages\jinja2\environment.py", line 925, in handle_exception

    raise rewrite_traceback_stack(source=source)
  • File "c:\Users\sosgx\Desktop\myweb\main\templates\view.html", line 1, in top-level template code

    {% extends "main.html" %}
  • File "c:\Users\sosgx\Desktop\myweb\main\templates\main.html", line 48, in top-level template code

    {% block contents %}
  • File "c:\Users\sosgx\Desktop\myweb\main\templates\view.html", line 49, in block 'contents'

    {% if len(list(comments.clone())) > 0 %}
  • File "C:\Users\sosgx\Desktop\myweb\venv\lib\site-packages\jinja2\utils.py", line 84, in from_obj

    if hasattr(obj, "jinja_pass_arg"):
jinja2.exceptions.UndefinedError: 'list' is undefined
The debugger caught an exception in your WSGI application. You can now look at the traceback which led to the error.

To switch between the interactive traceback and the plaintext one, you can click on the "Traceback" headline. From the text traceback you can also create a paste of it. For code execution mouse-over the frame you want to debug and click on the console icon on the right side.

You can execute arbitrary Python code in the stack frames and there are some extra helpers available for introspection:

  • dump() shows all variables in the frame
  • dump(obj) dumps all that's known about the object

 

 

------------------------------------------------------------------------

 

-view.html-

 

 

 

{% extends "main.html" %}

{% block contents %}

<div style="padding: 50px 50px 50px 50px;">
    <table class="table table-bordered">
        <tbody>
            <tr>
                <td colspan="2">{{result.title}}</td>
            </tr>
            <tr>
                <td>{{result.name}}</td>
                <td class="text-right">{{result.pubdate|formatdatetime}}</td>
            </tr>
            {% if result.attachfile %}
            <tr>
                <td>첨부파일</td>
                <td><a href="{{url_for('board.board_files', filename=result.attachfile)}}">{{result.attachfile}}</a></td>
            </tr>
            {% endif %}
            <tr>
                <td colspan="2"><div style="min-height: 200px;"></div>{% autoescape false %}{{result.contents}}{% endautoescape %}</td>
            </tr>
        </tbody>
    </table>

    <a class="btn btn-primary" href="{{url_for('board.lists', page=page, search=search, keyword=keyword)}}">리스트</a>

    {% if session["id"] == result.writer_id %}
    <a class="btn btn-danger float-right ml-2" href="{{url_for('board.board_delete', idx=result.id)}}">글삭제</a>
    <a class="btn btn-warning float-right" href="{{url_for('board.board_edit', idx=result.id)}}">글수정</a>
    {% endif %}
    <br>
    <br>
    <form id="commentForm" name="commentForm" action="{{url_for('board.comment_write')}}" method="POST">
        <input type="hidden" name="csrf_token" value="{{csrf_token()}}">
        <input type="hidden" name="root_idx" value="{{result.id}}">
        <div>
            <span><strong>댓글</strong></span> <span id="cCnt"></span>
            <table class="table">
                <tr>
                    <td><textarea rows="3" cols="110" id="comment" name="comment" placeholder="댓글을 입력하세요"></textarea></td>
                    <td><input type="submit" class="btn btn-success" style="height: 80px;" value="등록하기"></td>
                </tr>
            </table>
        </div>
    </form>

    {% if len(list(comments.clone())) > 0 %}
        {% for c in comments %}
            <div>
                <table class="table">
                    <tr>
                        <td width="100"><h6>{{c.name}}</h6></td>
                        <td>{{c.comment}}</td>
                        <td class="text-right" width="200">{{c.pubdate | formatdatetime}}</td>
                    </tr>
                </table>
            </div>
        {% endfor %}
    {% endif %}
</div>

{% endblock %}