파이썬 Flask 웹개발 프로젝트 1/3
파이썬 Flask 기반 웹프로그래밍 프로젝트 1/3
본 과정은 파이썬 Flask 를 기반으로 웹진 사이트를 제작하고 관리페이지에서 콘텐츠 및 회원을 관리하는 프로젝트 실습 중심의 과정 입니다.
하나씩 따라해서 실습해 보시기 바랍니다.
프로젝트 1/3 과정
– 관리페이지(DeshBoard) 만들기 1단계
– 로그인 페이지 만들기
1. pythonanywhere 새로운 계정으로 회원가입 및 로그인https://www.pythonanywhere.com/pricing/
저는 기존 아이디 aiclub -> 이번 수업용은 aiclub2로 만들었어어 (당연히 사용안한 다른 이메일로)
회원가입 후에는 이메일로 가셔서 승인 메일 확인하시는거 잊지 마시구요.
저의 경우 해외에서 수신된 메일이라 스팸 메일함으로 빠졌더군요 ㅜㅜ
2. Flask 개발환경 빠르게 구축하기
1) Console 상단 메뉴 클릭 > Bash 클릭
2) 가상환경 만들기명령어 :
mkvirtualenv --python=/usr/bin/python3.6 my-virtualenv
3) Flask 설치명령어 :
pip install flask
4) 상단 메뉴에서 Web 클릭 (새창)
– Add a new web app 클릭
– Next 클릭
– Flask 클릭
– Python 3.6 클릭 (이번에는 최신버전으로 해봅시다)
– Next 클릭
– 다시 Web 메뉴 클릭 > Vitualenv : 항목에 Enter path …. 이곳에
/home/당신의ID입력/.virtualenvs/my-virtualenv
입력 후 V 클릭- Reload 클릭후 사이트 접속 확인
3. 데이터베이스 회원 테이블 만들기
1) pymysql 설치를 위해 가상환경 콘솔
실행명령어 :
pip install pymysql
2) 상단 메뉴에 DataBase 클릭데이터베이스 비밀번호 입력후 생성
* 2분정도 기다려도 반응이 없으면 그냥 상단 메뉴에 DataBase를 클릭 하시면 됩니다.
* 회원가입 후 이메일 인증 안하고 데이터베이스 생성하면 꼬일수 있으니 꼭 인증을 먼저 하세요!
3) Start a console on 클릭
4) 회원 테이블 만들기
5) 관리자 회원 추가
– 프로필로 사용할 이미지를 /static/upload 폴더를 만들어서 member.png 파일로 업로드
https://www.iconfinder.com/
영어로 face 검색 후 free 이미지 하나 받아서 사용
– 쿼리 실행
insert into member set userid = 'admin', userpw = password('1234'), name = '이경용', img = 'member.png';
4. BootStrap 로그인 테마 활용하기
1) 관리 페이지 로그인 템플릿 적용
https://colorlib.com/wp/template/login-form-v18/
* 해당 템플릿을 사용해서 적용첨부파일 :
Login_v18.zip (템플릿 원본)적용결과 :
http://aiclub2.pythonanywhere.com/admin
(아이디 : admin / 비밀번호 : 1234)
2) 로그인 테마 스테틱 파일 업로드
– mysite에 static 폴더 만들기
– static 폴더에 login 폴더 만들기
– login 폴더에 로그인 테마 스테틱 관련 파일들을 login.zip으로 압축해서 파일 업로드하기
– 가상환경에 접속해서
cd ~/mysite/static/login
unzip login.zip
– 실행 (압축 풀고 나면 zip파일 삭제 해도 됨)
– css 파일 중에 경로 수정하기 /static 절대 경로로 적용 (바꾸기 단축키 Ctrl + H)
3) 템플릿 파일 만들기
/templates/admin/login.html
로그인 폼 페이지
– css, js, img 파일 경로를 /static/login 으로 수정 (Ctrl + H)
<!DOCTYPE html>
<html lang="ko">
<head>
<title>Admin Login</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="/static/login/images/icons/favicon.ico?v=1"/>
<link rel="stylesheet" type="text/css" href="/static/login/vendor/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="/static/login/fonts/font-awesome-4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="/static/login/fonts/Linearicons-Free-v1.0.0/icon-font.min.css">
<link rel="stylesheet" type="text/css" href="/static/login/vendor/animate/animate.css">
<link rel="stylesheet" type="text/css" href="/static/login/vendor/css-hamburgers/hamburgers.min.css">
<link rel="stylesheet" type="text/css" href="/static/login/vendor/animsition/css/animsition.min.css">
<link rel="stylesheet" type="text/css" href="/static/login/vendor/select2/select2.min.css">
<link rel="stylesheet" type="text/css" href="/static/login/vendor/daterangepicker/daterangepicker.css">
<link rel="stylesheet" type="text/css" href="/static/login/css/util.css">
<link rel="stylesheet" type="text/css" href="/static/login/css/main.css">
</head>
<body style="background-color: #666666;">
<div class="limiter">
<div class="container-login100">
<div class="wrap-login100">
<form action="/admin" method="post" class="login100-form validate-form">
<span class="login100-form-title p-b-43">
Login to continue
</span>
<div class="wrap-input100 validate-input" data-validate = "Valid ID is required: abcd">
<input class="input100" type="text" name="userid">
<span class="focus-input100"></span>
<span class="label-input100">Email</span>
</div>
<div class="wrap-input100 validate-input" data-validate="Password is required">
<input class="input100" type="password" name="userpw">
<span class="focus-input100"></span>
<span class="label-input100">Password</span>
</div>
<div class="flex-sb-m w-full p-t-3 p-b-32">
<div class="contact100-form-checkbox">
<input class="input-checkbox100" id="ckb1" type="checkbox" name="remember-me">
<label class="label-checkbox100" for="ckb1">
Remember me
</label>
</div>
<div>
<a href="/static/login/#" class="txt1">
Forgot Password?
</a>
</div>
</div>
<div class="container-login100-form-btn">
<button class="login100-form-btn">
Login
</button>
</div>
{% if msg %}
<div style="padding-top:10px;">
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<strong>{{msg}}</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</div>
{% endif %}
<div class="text-center p-t-46 p-b-20">
<span class="txt2">
or sign up using
</span>
</div>
<div class="login100-form-social flex-c-m">
<a href="/static/login/#" class="login100-form-social-item flex-c-m bg1 m-r-5">
<i class="fa fa-facebook-f" aria-hidden="true"></i>
</a>
<a href="/static/login/#" class="login100-form-social-item flex-c-m bg2 m-r-5">
<i class="fa fa-twitter" aria-hidden="true"></i>
</a>
</div>
</form>
<div class="login100-more" style="background-image: url('/static/login/images/bg-01.jpg?v=1');">
</div>
</div>
</div>
</div>
<script src="/static/login/vendor/jquery/jquery-3.2.1.min.js"></script>
<script src="/static/login/vendor/animsition/js/animsition.min.js"></script>
<script src="/static/login/vendor/bootstrap/js/popper.js"></script>
<script src="/static/login/vendor/bootstrap/js/bootstrap.min.js"></script>
<script src="/static/login/vendor/select2/select2.min.js"></script>
<script src="/static/login/vendor/daterangepicker/moment.min.js"></script>
<script src="/static/login/vendor/daterangepicker/daterangepicker.js"></script>
<script src="/static/login/vendor/countdowntime/countdowntime.js"></script>
<script src="/static/login/js/main.js"></script>
</body>
</html>
4) /template/admin/base.html 템플릿 파일 만들기
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ADMIN</title>
<!-- BootStrap CDN -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
5) /templates/admin/main.html 관리자 페이지 만들기
{% extends "/admin/base.html" %}
{% block content %}
<h1>{{member['ss_name']}}님(보안 : {{member['ss_level']}} 급) 관리페이지에 오신것을 환영합니다.</h1>
<img src="/static/upload/{{member['ss_img']}}" class="img-thumbnail">
<div>
<a href="/admin/logout" class="btn btn-success">로그아웃</a>
</div>
{% endblock %}
6) /flask_app.py 수정
from flask import Flask, render_template, request, redirect, session
import pymysql
import time
app = Flask(__name__)
app.secret_key = 'aiclub'
@app.route('/admin', methods=['GET','POST'])
def admin():
if request.method == "GET":
return render_template('/admin/login.html')
else:
userid = request.form['userid']
userpw = request.form['userpw']
db = pymysql.connect(
host = 'UserID.mysql.pythonanywhere-services.com',
port = 3306,
user = 'UserID',
passwd = 'DB Password',
db = 'UserID$default',
charset = 'utf8'
)
cursor = db.cursor()
sql = """ select name, level, img from member
where userid = '%s' and userpw = password('%s');
""" % (userid, userpw)
cursor.execute(sql)
rows = cursor.fetchone()
db.close
if rows:
session['ss_id'] = userid
session['ss_name'] = rows[0]
session['ss_level'] = rows[1]
session['ss_img'] = rows[2]
return redirect('/admin/main')
else:
return render_template('/admin/login.html',
msg="아이디 또는 비밀번호를 잘못 입력하셨습니다.")
@app.route('/admin/main')
def admin_main():
if session['ss_id'] == False:
return redirect('/admin')
else:
member = {
"ss_name":session['ss_name'],
"ss_level":session['ss_level'],
"ss_img":session['ss_img']
}
return render_template('/admin/main.html',
member=member)
@app.route('/admin/logout')
def admin_logout():
session['ss_id'] = False
session['ss_name'] = False
session['ss_level'] = False
session['ss_img'] = False
return redirect('/admin')
/login : 로그인 처리 (템플릿 없음)
/logout : 로그아웃 처리 (템플릿 없음)
/admim : 관리페이지
6. 최종 결과물
http://aiclub2.pythonanywhere.com/login
해당 게시글에서 코드가 보기 힘들면 네이버 카페에 올려 놓은 내용을 보시면 됩니다.
단, PC에 최적화 되어 있으니 PC에서 접속해서 보시기 바랍니다.