묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결
질문답변이 가능한 웹사이트 개발, 어떤 인프라가 적절할까요?
안녕하세요 선생님들 :) 사이드프로젝트로 react와 GraphQL을 공부하여 Gatsby로 블로그를 개발중에 있습니다. 다음 추가기능으로는 질문답변 기능을 넣어보고싶어서요 그러려면 로그인 기능도 필요할테고, 그러면 동적 코드들이 많아질것 같은데요 생각하는 기능들을 추가하려면, (내 질문글 보기, 내 답변 보기등등.. 그러면 매우 동적인 프로필컴포넌트도 추가될것같구...) 굳이 개츠비에서 업그레이드하는게 맞나? 하는 생각도 드는데요. 만약 다른 프레임워크를 찾아본다면 프론트, 서버, 호스팅, 배포단에서 추천해주실만한 프레임웤이나 인프라가 있을까요? 툭툭 던져주시면 자세한건 제가 찾아보면서 공부해보겠습니다! 고수님들 조언 부탁드려요 ㅠ-ㅠ
-
미해결남박사의 파이썬으로 실전 웹사이트 만들기
글 작성 폼에 HTML 에디터 기능 추가하기 이미지 저장 오류..ㅠ
남박사님 계속 질문해서 죄송합니다..ㅠ 하다보니까 계속 오류가 나네요.... 이미지 업로드 관련해서 ajax 부분에서 오류가 나는것은 영상을 따라 하면서 해봐도 계속 같은 오류가 나와서 답답해서 질문드립니다. - 오류1 - 영상대로 수정하고 적용해서 똑같은 오류가 나오더라구요.. 그래서 이미지 업로드창 눌러서 해봤는데 비슷한 오류가 나옵니다. -오류2- -오류3- - 코드- -write.html- {% extends "main.html" %} {% block contents %} <script> $(document).ready(function() { $("#summernote").summernote({ height: 300, minHeight: null, maxHeight: null, lang: "ko-KR", popover: { image:[], link:[], air:[] }, callbacks: { onImageUpload: function(image) { for(var i = 0; i < image.length; i++) { uploadImage(image[i]); } } } }); }); function uploadImage(image) { var data = new FormData(); data.append("image", image); $.ajax({ url: "{{url_for('board.upload_image')}}", cache: false, contentType: false, processData: false, data: data, type:"post", success: function(url) { var image = $("<img>").attr("src",url).css('max-width', "900px"); $("#summernote").summernote("insertNode",image[0]); }, error: function(data) { console.log(data); alert(data); } }); } </script> <script> function checkForm() { if ($.trim($("#title").val()) == "") { alert("제목을 입력하세요"); $("#title").focus(); return false; } if ($.trim($("#summernote").val()) == "") { alert("내용을 입력하세요"); $("#summernote").focus(); return false; } } </script> <div style="padding: 50px 50px 50px 50px;"> <form name="form" method="post" action="{{url_for('board.board_write')}}" onsubmit="return checkForm()"> <div class="form-group"> <label for="name">작성자</label> <input class="form-control" id="name" type="text" name="name" value="{{session['name']}}" readonly> </div> <div class="form-group"> <label for="title">제목</label> <input class="form-control" type="text" name="title" id="title" placeholder="제목을 입력하세요"/> </div> <div class="form-group"> <label for="contents">내용</label> <textarea rows="8" class="form-control" type="text" name="contents" id="summernote" placeholder="내용을 입력하세요"></textarea> </div> <div class="text-center"><input class="btn btn-primary" type="submit" value="작성하기"></div> </form> </div> {% endblock %} --------------------------------------------------------------------------------------- -viev.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> <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 %} </div> {% endblock %} --------------------------------------------------------------------------------------------------- -main.html- <!DOCTYPE html> <html lang="kr"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{title}}</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous"> <script src="https://code.jquery.com/jquery-3.6.0.min.js" type="text/javascript"></script> <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script> <!-- include summernote css/js --> <link href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote.min.css" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote.min.js"></script> <script> function validateEmail(email) { var pattern = /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/; return pattern.test(email); } </script> {% with messages = get_flashed_messages() %} {% if messages %} <script> alert('{{messages[-1]}}'); </script> {% endif %} {% endwith %} </head> <body> <div class="containter"> {% include "menu.html" %} {% block contents %} {% endblock %} </div> </body> </html> -------------------------------------------------------------------------------------------------- -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") @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("/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", "") } return render_template( "view.html", result=result, page=page, search=search, keyword=keyword, title="글 상세보기") return abort(404) @bluerprint.route("/write", methods=["GET", "POST"]) def board_write(): if session.get("id") is None: return redirect(url_for("member.member_login")) if request.method == "POST": name = request.form.get("name") title = request.form.get("title") contents = request.form.get("contents") print(name, title, contents) 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, } 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") board = mongo.db.board data = board.find_one({"_id": ObjectId(idx)}) if session.get("id") == data.get("writer_id"): board.update_one({"_id": ObjectId(idx)}, { "$set": { "title": title, "contents": contains, } }) 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")) --------------------------------------------------------------------------------- -__init__.py- from flask import Flask from flask import request from flask import render_template from flask_pymongo import PyMongo from bson.objectid import ObjectId from datetime import datetime, timedelta from flask import abort from flask import redirect from flask import url_for from flask import flash from flask import session import math import time import os app = Flask(__name__) app.config["MONGO_URI"] = "mongodb://localhost:27017/myweb" app.config["SECRET_KEY"] = "abcd" app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(minutes=30) mongo = PyMongo(app) BOARD_IMAGE_PATH = "C:\\python\\images" ALLOWED_EXTENSIONS =set(["txt", "pdf", "png", "jpg", "jpeg", "gif"]) app.config["BOARD_IMAGE_PATH"] = BOARD_IMAGE_PATH app.config["MAX-CONTENT_LENGTH"] = 15 * 1024 * 1024 if not os.path.exists(app.config["BOARD_IMAGE_PATH"]): os.mkdir(app.config["BOARD_IMAGE_PATH"]) # 아래의 파일들에서 main 의 app 에 접근을 해야하니 # 아래 import 위에 app 가 선언되어야 합니다. from .common import login_required, allowed_file, rand_generator from .filter import format_datetime # member, board 에서 login_required, format_datetime 을 사용하니까 # 당연히 member 와 board 가 위의 import 보다 먼저 나와선 안됩니다. from . import member from . import board app.register_blueprint(board.bluerprint) app.register_blueprint(member.bluerprint)
-
해결됨남박사의 파이썬으로 실전 웹사이트 만들기
(board_edit -> if data in None: 오류)오류나는 부분이 있어서 다시 질문 드립니다!
안녕하세요 남박사님! 저번에 질문 올렸는데요!.... 플라스크 프로젝트를 모듈화 하여 패키징 하기 부분에서 오류나서 질문을 드렸었습니다! 죄송합니다만 .... 댓글 달아주셨는데.. 제가 궁금한 부분도 다시 댓글 달았습니다. 답변 부탁드리겠습니다. 지금 진도는 javascript, iQuery 폼 검증 기능 추가하기 입니다. 모듈화 부분에서 전부 모듈화 하고... venv 들어가 있는 상태에서 재생버튼 눌러서 강제로 실행하니까.. 코드 오류 무시하고 실행이 되더라구요...! 왜그런지는 모르겠습니다! 실행이되서 진도를 나갔는데 다른 부분은 전부 정상적으로 적용되는데.. 한곳에서 적용이 안되는 구간이 있어서 질문 올립니다! -코드 오류 -1 - -코드 오류-2 - -글목록 수정- 글목록 작성한다음에 글수정 누르니까 오류가 나옵니다. -웹페이지 오류- -run.py- from main import app if __name__ == "__main__": app.run(host="0.0.0.0", debug=True, port=9000) ---------------------------------------------------------------------------------------------------- -__init__.py- from flask import Flask from flask import request from flask import render_template from flask_pymongo import PyMongo from bson.objectid import ObjectId from datetime import datetime, timedelta from flask import abort from flask import redirect from flask import url_for from flask import flash from flask import session import math import time app = Flask(__name__) app.config["MONGO_URI"] = "mongodb://localhost:27017/myweb" app.config["SECRET_KEY"] = "abcd" app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(minutes=30) mongo = PyMongo(app) # 아래의 파일들에서 main 의 app 에 접근을 해야하니 # 아래 import 위에 app 가 선언되어야 합니다. from .filter import format_datetime from .common import login_required # member, board 에서 login_required, format_datetime 을 사용하니까 # 당연히 member 와 board 가 위의 import 보다 먼저 나와선 안됩니다. from . import member from . import board app.register_blueprint(board.bluerprint) app.register_blueprint(member.bluerprint) -------------------------------------------------------------------------------------------- -board.py- from turtle import title from main import * from flask import Blueprint bluerprint = Blueprint("board", __name__, url_prefix="/board") @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", "") } return render_template( "view.html", result=result, page=page, search=search, keyword=keyword, title="글 상세보기") return abort(404) @bluerprint.route("/write", methods=["GET", "POST"]) def board_write(): if session.get("id") is None: return redirect(url_for("member.member_login")) if request.method == "POST": name = request.form.get("name") title = request.form.get("title") contents = request.form.get("contents") print(name, title, contents) 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, } 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 in 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") board = mongo.db.board data = board.find_one({"_id": ObjectId(idx)}) if session.get("id") == data.get("writer_id"): board.update_one({"_id": ObjectId(idx)}, { "$set": { "title": title, "contents": contains, } }) 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")) --------------------------------------------------------------------------------- -member.py- from turtle import title from main import * from flask import Blueprint bluerprint = Blueprint("member", __name__, url_prefix="/member") @bluerprint.route("/join", methods=["GET", "POST"]) def member_join(): if request.method == "POST": name = request.form.get("name", type=str) email = request.form.get("email", type=str) pass1 = request.form.get("pass", type=str) pass2 = request.form.get("pass2", type=str) if name == "" or email == "" or pass1 == "" or pass2 == "": flash("입력되지 않은 값이 있습니다.") return render_template("join.html", title="회원가입") if pass1 != pass2: flash("비밀번호가 일치하지 않습니다.") return render_template("join.html", title="회원가입") members = mongo.db.members cnt = members.count_documents({"email": email}) if cnt > 0: flash("중복된 이메일 주소입니다.") return render_template("join.html", title="회원가입") current_utc_time = round(datetime.utcnow().timestamp() * 1000) post = { "name": name, "email": email, "pass": pass1, "joindate": current_utc_time, "logintime": "", "logincount": 0, } members.insert_one(post) return redirect(url_for("member.member_login")) else: return render_template("join.html", title="회원가입") @bluerprint.route("/login", methods=["GET", "POST"]) def member_login(): if request.method == "POST": email = request.form.get("email") password = request.form.get("pass") next_url = request.form.get("next_url") members = mongo.db.members data = members.find_one({"email": email}) if data is None: flash("회원 정보가 없습니다.") return redirect(url_for("member.member_login")) else: if data.get("pass") == password: session["email"] = email session["name"] = data.get("name") session["id"] = str(data.get("_id")) session.permanent = True if next_url is not None: return redirect(next_url) else: return redirect(url_for("board.lists")) return redirect(url_for("board.lists")) else: flash("비밀번호가 일치하지 않습니다.") return redirect(url_for("member.member_login")) return "" else: next_url = request.args.get("next_url", type=str) if next_url is not None: return render_template("login.html", next_url=next_url, title="회원로그인") else: return render_template("login.html", title="회원로그인") @bluerprint.route("/logout") def member_logout(): try: del session["name"] del session["id"] del session["email"] except: pass return redirect(url_for('member.member_login')) ---------------------------------------------------------------------------------- -main.html- <!DOCTYPE html> <html lang="kr"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{title}}</title> {% with messages = get_flashed_messages() %} {% if messages %} <script> alert('{{messages[-1]}}'); </script> {% endif %} {% endwith %} </head> <body> {% include "menu.html" %} {% block contents %} {% endblock %} </body> </html> --------------------------------------------------------------------------- -edit.html- {% extends "main.html" %} {% block contents %} <table> <form name="form" method="post" action="{{url_for('board.board_edit', idx=data._id)}}"> <tr> <td>작성자</td> <td><input type="text" name="name" value="{{session['name']}}" readonly /></td> </tr> <tr> <td>제목</td> <td><input type="text" name="title" /></td> </tr> <tr> <td>내용</td> <td><textarea type="text" name="contents">{{data.contents}}</textarea></td> </tr> <tr> <td colspan="2"><input type="submit" /></td> </tr> </form> </table> {% endblock %} ------------------------------------------------------------------------- -join.html- {% extends "main.html" %} {% block contents %} <table> <form name="form" action="/join" method="POST"> <thead> <caption> 회원가입 </caption> </thead> <tbody> <tr> <td>이름</td> <td><input type="text" name="name" /></td> </tr> <tr> <td>이메일</td> <td><input type="text" name="email" /></td> </tr> <tr> <td>비밀번호</td> <td><input type="password" name="pass" /></td> </tr> <tr> <td>비밀번호 확인</td> <td><input type="password" name="pass2" /></td> </tr> <tr> <td colspan="2"><input type="submit" value="가입하기" /></td> </tr> </tbody> </form> </table> {% endblock %} ------------------------------------------------------------------------------- -list.html- {% extends "main.html" %} {% block contents %} <script> function search() { var v_search = document.getElementById("search").value; var v_keyword = document.getElementById("keyword").value; if (v_search == "" || v_keyword == "") { return false; } else { self.location.href = "{{url_for('board.lists')}}?search=" + v_search + "&keyword=" + v_keyword; } } </script> {% if datas|length > 0 %} <table> <thead> <tr> <td>번호</td> <td>제목</td> <td>이름</td> <td>날짜</td> <td>조회수</td> </tr> </thead> <tbody> <!--반복되는 구간--> <tr> {% for data in datas %} <td>{{loop.index + ((page - 1) * limit)}}</td> <td> <a href="{{url_for('board.board_view', idx=data._id, page=page, search=search, keyword=keyword)}}">{{data.title}}</a> </td> <td>{{data.name}}</td> <td>{{data.pubdate|formatdatetime}}</td> <td>{{data.view}}</td> </tr> {% endfor %} <!--반복되는 구간 끝--> </tbody> </table> {% if block_start - 1 > 0 %} <a href="{{url_for('board.lists', page=block_start - 1, search=search, keyword=keyword)}}">[이전]</a> {% endif %} {% for i in range(block_start, block_last + 1) %} {% if i > last_page_num %} {{ i }} {% else %} {% if i == page %} <b>{{ i }}</b> {% else %} <a href="{{url_for('board.lists', page=i, search=search, keyword=keyword)}}">{{ i }}</a> {% endif %} {% endif %} {% endfor %} {% if block_last < last_page_num %} <a href="{{url_for('board.lists', page=block_last + 1, search=search, keyword=keyword)}}">[다음]</a> {% endif %} <select name="search" id="search"> <option value="" {% if search=='' or search==-1 %} selected {% endif %}>검색대상</option> <option value="0" {% if search==0 %} selected {% endif %}>제목</option> <option value="1" {% if search==1 %} selected {% endif %}>내용</option> <option value="2" {% if search==2 %} selected {% endif %}>제목+내용</option> <option value="3" {% if search==3 %} selected {% endif %}>작성자</option> </select> <input type="text" name="keyword" id="keyword" {% if keyword !="" %} value="{{keyword}}" {% endif %} /> <input type="button" value="검색" onclick="search()" /> {% else %} <h3>데이터가 없습니다.</h3> {% endif %} <a href="{{url_for('board.board_write')}}">글작성</a> {% endblock %} -------------------------------------------------- -login.html- {% extends "main.html" %} {% block contents %} <table> <form name="form" action="{{url_for('member.member_login')}}" method="POST"> {% if next_url %} <input type="hidden" name="next_url" value="{{next_url}}" /> {% endif %} <thead> <caption> 회원 로그인 </caption> </thead> <tbody> <tr> <td>이메일</td> <td><input type="text" name="email" /></td> </tr> <tr> <td>비밀번호</td> <td><input type="password" name="pass" /></td> </tr> <tr> <td colspan="2"><input type="submit" value="로그인" /></td> </tr> </tbody> </form> </table> {% endblock %} ---------------------------------------------- -view.html- {% extends "main.html" %} {% block contents %} {% with messages = get_flashed_messages() %} {% if messages %} <script> alert("{{messages[-1]}}"); </script> {% endif %} {% endwith %} {{result.title}} <br /> {{result.name}} <br /> {{result.pubdate|formatdatetime}} <br /> {{result.view}} <br /> {{result.contents}} <br /> <a href="{{url_for('board.lists', page=page, search=search, keyword=keyword)}}" >리스트</a > {% if session["id"] == result.writer_id %} <a href="{{url_for('board.board_delete', idx=result.id)}}">글삭제</a> <a href="{{url_for('board.board_edit', idx=result.id)}}">글수정</a> {% endif %} {% endblock %} ------------------------- -write.html- {% extends "main.html" %} {% block contents %} <table> <form name="form" method="post" action="{{url_for('board.board_write')}}"> <tr> <td>작성자</td> <td><input type="text" name="name" value="{{session['name']}}" readonly /></td> </tr> <tr> <td>제목</td> <td><input type="text" name="title" /></td> </tr> <tr> <td>내용</td> <td><textarea type="text" name="contents"></textarea></td> </tr> <tr> <td colspan="2"><input type="submit" /></td> </tr> </form> </table> {% endblock %}
-
해결됨남박사의 파이썬으로 실전 웹사이트 만들기
오류 나는 부분이 있어서 다시 질문 드립니다! 도와주세요! (join 로그인시 백지, 몽고db 이메일주소 null 표시)
안녕하세요 웹개발 입문부분 강좌를 거의다 들었는데.. 오류가 나는 부분이 있어서.. 질문을 올립니다! 문제는..join 로그인 하면 몽고db에서 이메일 주소가 null로 표시가 되서.. 동작이 되지 않는점이 있습니다.. 비슷한 증상을 찾기 어려워서 질문 남깁니다! -화면- -join- -mongoDB- -list- -list 로그인 페이지- next_url << 이게 문제가 나왔었는데 다른 코드들 참고 했는데 해결은 됬습니다. -run.py- from flask import Flask from flask import request from flask import render_template from flask_pymongo import PyMongo from bson.objectid import ObjectId from flask import abort from flask import redirect from flask import url_for from flask import flash from flask import session from functools import wraps import math import time from datetime import datetime, timedelta app = Flask(__name__) app.config["MONGO_URI"] = "mongodb://localhost:27017/myweb" app.config["SECRET_KEY"] = "abcd" app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(minutes=30) mongo = PyMongo(app) def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): if session.get("id") is None or session.get("id") == "": return redirect(url_for("member_login", next_url=request.url)) return f(*args, **kwargs) return decorated_function @app.template_filter('formatdatetime') def format_datetime(value): if value is None: return "" now_timestamp = time.time() offset = datetime.fromtimestamp( now_timestamp) - datetime.utcfromtimestamp(now_timestamp) value = datetime.fromtimestamp((int(value) / 1000)) + offset return value.strftime('%Y-%m-%d %H:%M:%S') @app.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) # 게시물의 총 갯수 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) @ app.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", "") } return render_template("view.html", result=result, page=page, search=search, keyword=keyword) return abort(404) @ app.route("/write", methods=["GET", "POST"]) def board_write(): if session.get("id") is None: return redirect(url_for("member_login")) if request.method == "POST": name = request.form.get("name") title = request.form.get("title") contents = request.form.get("contents") print(name, title, contents) 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, } x = board.insert_one(post) print(x.inserted_id) return redirect(url_for("board_view", idx=x.inserted_id)) else: return render_template("write.html") @app.route("/join", methods=["GET", "POST"]) def member_join(): if request.method == "POST": name = request.form.get("name", type=str) email = request.form.get("email", type=str) pass1 = request.form.get("pass", type=str) pass2 = request.form.get("pass2", type=str) if name == "" or email == "" or pass1 == "" or pass2 == "": flash("입력되지 않은 값이 있습니다.") return render_template("join.html") if pass1 != pass2: flash("비밀번호가 일치하지 않습니다.") return render_template("join.html") members = mongo.db.members cnt = members.count_documents({"email": email}) if cnt > 0: flash("중복된 이메일 주소입니다.") return render_template("join.html") current_utc_time = round(datetime.utcnow().timestamp() * 1000) post = { "name": name, "email": email, "pass": pass1, "joindate": current_utc_time, "logintime": "", "logincount": 0, } members.insert_one(post) return "" else: return render_template("join.html") @app.route("/login", methods=["GET", "POST"]) def member_login(): if request.method == "POST": email = request.form.get("email") password = request.form.get("pass") next_url = request.form.get("next_url") members = mongo.db.members data = members.find_one({"email": email}) if data is None: flash("회원 정보가 없습니다.") return redirect(url_for("member_login")) else: if data.get("pass") == password: session["email"] = email session["name"] = data.get("name") session["id"] = str(data.get("_id")) session.permanent = True if next_url is not None: return redirect(next_url) else: return redirect(url_for("lists")) return redirect(url_for("lists")) else: flash("비밀번호가 일치하지 않습니다.") return redirect(url_for("member_login")) return "" else: next_url = request.args.get("next_url", type=str) if next_url is not None: return render_template("login.html", next_url=next_url) else: return render_template("login.html") @app.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 in None: flash("해당 게시물이 존재하지 않습니다.") return redirect(url_for("lists")) else: if session.get("id") == data.get("writer_id"): return render_template("edit.html", data=data) else: flash("글 수정 권한이 없습니다.") return redirect(url_for("lists")) else: title = request.form.get("title") contains = request.form.get("contents") board = mongo.db.board data = board.find_one({"_id": ObjectId(idx)}) if session.get("id") == data.get("writer_id"): board.update_one({"_id": ObjectId(idx)}, { "$set": { "title": title, "contents": contains, } }) flash("수정 되었습니다.") return redirect(url_for("board_view", idx=idx)) else: flash("글 수정 권한이 없습니다.") return redirect(url_for("lists")) @app.route("/delete/<idx>") def board_delete(idx): return "" if __name__ == "__main__": app.run(host="0.0.0.0", debug=True, port=9000) ------------------------------------------------------------------------------------------------ -join- {% with messages = get_flashed_messages() %} {% if messages %} <script> alert('{{messages[-1]}}'); </script> {% endif %} {% endwith %} <table> <form name="form" action="/join" method="POST"> <thead> <caption>회원가입</caption> </thead> <tbody> <tr> <td>이름</td> <td><input type="text" name="name"></td> </tr> <tr> <td>이메일</td> <td><input type="text" nmae="email"></td> </tr> <tr> <td>비밀번호</td> <td><input type="password" name="pass"></td> </tr> <tr> <td>비밀번호 확인</td> <td><input type="password" name="pass2"></td> </tr> <tr> <td colspan="2"><input type="submit" value="가입하기"></td> </tr> </tbody> </form> </table> ---------------------------------------------------------------------------------------------------------- -login- {% with messages = get_flashed_messages() %} {% if messages %} <script> alert("{{messages[-1]}}"); </script> {% endif %} {% endwith %} <table> <form name="form" action="/login" method="POST"> {% if next_url %} <input type="hidden" name="next_url" value="{{next_url}}" /> {% endif %} <thead> <caption> 회원 로그인 </caption> </thead> <tbody> <tr> <td>이메일</td> <td><input type="text" name="email" /></td> </tr> <tr> <td>비밀번호</td> <td><input type="password" name="pass" /></td> </tr> <tr> <td colspan="2"><input type="submit" value="로그인" /></td> </tr> </tbody> </form> </table> -------------------------------------------------------------------------------------------------------- - list - {% with messages = get_flashed_messages() %} {% if messages %} <script> alert('{{messages[-1]}}'); </script> {% endif %} {% endwith %} <script> function search() { var v_search = document.getElementById("search").value; var v_keyword = document.getElementById("keyword").value; if (v_search == "" || v_keyword == "") { return false; } else { self.location.href = "{{url_for('lists')}}?search=" + v_search + "&keyword=" + v_keyword; } } </script> {% if datas|length > 0 %} <table> <thead> <tr> <td>번호</td> <td>제목</td> <td>이름</td> <td>날짜</td> <td>조회수</td> </tr> </thead> <tbody> <!--반복되는 구간--> <tr> {% for data in datas %} <td>{{loop.index + ((page - 1) * limit)}}</td> <td> <a href="{{url_for('board_view', idx=data._id, page=page, search=search, keyword=keyword)}}">{{data.title}}</a> </td> <td>{{data.name}}</td> <td>{{data.pubdate|formatdatetime}}</td> <td>{{data.view}}</td> </tr> {% endfor %} <!--반복되는 구간 끝--> </tbody> </table> {% if block_start - 1 > 0 %} <a href="{{url_for('lists', page=block_start - 1, search=search, keyword=keyword)}}">[이전]</a> {% endif %} {% for i in range(block_start, block_last + 1) %} {% if i > last_page_num %} {{ i }} {% else %} {% if i == page %} <b>{{ i }}</b> {% else %} <a href="{{url_for('lists', page=i, search=search, keyword=keyword)}}">{{ i }}</a> {% endif %} {% endif %} {% endfor %} {% if block_last < last_page_num %} <a href="{{url_for('lists', page=block_last + 1, search=search, keyword=keyword)}}">[다음]</a> {% endif %} <select name="search" id="search"> <option value="" {% if search=='' or search==-1 %} selected {% endif %}>검색대상</option> <option value="0" {% if search==0 %} selected {% endif %}>제목</option> <option value="1" {% if search==1 %} selected {% endif %}>내용</option> <option value="2" {% if search==2 %} selected {% endif %}>제목+내용</option> <option value="3" {% if search==3 %} selected {% endif %}>작성자</option> </select> <input type="text" name="keyword" id="keyword" {% if keyword !="" %} value="{{keyword}}" {% endif %} /> <input type="button" value="검색" onclick="search()" /> {% else %} <h3>데이터가 없습니다.</h3> {% endif %} <a href="{{url_for('board_write')}}">글작성</a> ------------------------------------------------------------------------------------------------------- - edit - <html> <body> <table> <form name="form" method="post" action="/edit/{{data._id}}"> <tr> <td>작성자</td> <td><input type="text" name="name" value="{{session['name']}}" readonly /></td> </tr> <tr> <td>제목</td> <td><input type="text" name="title" /></td> </tr> <tr> <td>내용</td> <td><textarea type="text" name="contents">{{data.contents}}</textarea></td> </tr> <tr> <td colspan="2"><input type="submit" /></td> </tr> </form> </table> </body> </html>
-
해결됨남박사의 파이썬으로 실전 웹사이트 만들기
회원가입 만들기 오류 문의 (AttributeError: 'Cursor' object has no attribute 'count') 오류
안녕하세요 몇일째 오류랑 씨름하다가 여기까지 왔는데.. 도저희 해결이 안나서 문의 남깁니다. 구글링이나.. 다른분들 오류 해결하시는거 찾아보면서 해도.. 계속오류 나면서 하니까 진도가 너무 늦네요..ㅠ 파이썬으로 실전 웹사이트 만들기 - 회원가입 페이지 만들기 중입니다. -오류내용- -코드- (run.py) from flask import Flask from flask import request from flask import render_template from flask_pymongo import PyMongo from bson.objectid import ObjectId from flask import abort from flask import redirect from flask import url_for from flask import flash import math import time from datetime import datetime app = Flask(__name__) app.config["MONGO_URI"] = "mongodb://localhost:27017/myweb" app.config["SECRET_KEY"] = "abcd" mongo = PyMongo(app) @app.template_filter('formatdatetime') def format_datetime(value): if value is None: return "" now_timestamp = time.time() offset = datetime.fromtimestamp( now_timestamp) - datetime.utcfromtimestamp(now_timestamp) value = datetime.fromtimestamp((int(value) / 1000)) + offset return value.strftime('%Y-%m-%d %H:%M:%S') @app.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) # 게시물의 총 갯수 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) @ app.route("/view/<idx>") 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)}) 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") } return render_template("view.html", result=result, page=page, search=search, keyword=keyword) return abort(404) @ app.route("/write", methods=["GET", "POST"]) def board_write(): if request.method == "POST": name = request.form.get("name") title = request.form.get("title") contents = request.form.get("contents") print(name, title, contents) current_utc_time = round(datetime.utcnow().timestamp() * 1000) board = mongo.db.board post = { "name": name, "title": title, "contents": contents, "pubdate": current_utc_time, "view": 0, } x = board.insert_one(post) print(x.inserted_id) return redirect(url_for("board_view", idx=x.inserted_id)) else: return render_template("write.html") @app.route("/join", methods=["GET", "POST"]) def member_join(): if request.method == "POST": name = request.form.get("name", type=str) email = request.form.get("email", type=str) pass1 = request.form.get("pass", type=str) pass2 = request.form.get("pass2", type=str) if name == "" or email == "" or pass1 == "" or pass2 == "": flash("입력되지 않은 값이 있습니다.") return render_template("join.html") if pass1 != pass2: flash("비밀번호가 일치하지 않습니다.") return render_template("join.html") members = mongo.db.members cnt = members.find({"email": email}).count() if cnt > 0: flash("중복된 이메일 주소입니다.") return render_template("join.html") current_utc_time = round(datetime.utcnow().timestamp(*1000)) post = { "name": name, "email": email, "pass": pass1, "joindate": current_utc_time, "logintime": " ", "logincount": 0 } members.insert_one(post) return "" else: return render_template("join.html") if __name__ == "__main__": app.run(host="0.0.0.0", debug=True, port=9000) (join.html) {% with messages = get_flashed_messages() %} {% if messages %} <script> alert('{{messages[-1]}}'); </script> {% endif %} {% endwith %} <table> <form name="form" action="/join" method="POST"> <thead> <caption>회원가입</caption> </thead> <tbody> <tr> <td>이름</td> <td><input type="text" name="name"></td> </tr> <tr> <td>이메일</td> <td><input type="text" nmae="email"></td> </tr> <tr> <td>비밀번호</td> <td><input type="password" name="pass"></td> </tr> <tr> <td>비밀번호 확인</td> <td><input type="password" name="pass2"></td> </tr> <tr> <td colspan="2"><input type="submit" value="가입하기"></td> </tr> </tbody> </form> </table>
-
미해결[하루 10분|Web Project] HTML/JS/CSS로 나만의 심리테스트 사이트 만들기
웹사이트 상위 노출 방법
강의를 기반으로 웹사이트를 하나 만들었는데 인터넷 (구글,네이버 등)에 아무리 검색해도 제가 웹사이트가 전혀 보이지 않아요. 웹사이트 링크로 직접 들어가서 확인해보니 웹사이트는 잘 돌아가고 있는데 제 웹사이트를 구글 검색 결과에 잘 보이게 하는 방법은 없을까요? 있다면 제발 알려주세요.ㅠㅠ
-
미해결웹브라우저 Javascript (자바스크립트)
sublime text 코드가 크롬이 아니라 마이크로소프트 엣지로 저장이 됩니다.
1강 실습방법에 나온 것 처럼 sublime text를 이용하여 코드를 만든 후 Ctrl+O를 눌러 코드를 들고 왔는데 코드가 크롬이 아니라 엣지로 저장이 되어있습니다. OS는 윈도우 10 입니다.