본 실습 리뷰는 아래 교재인 django 한그릇뚝딱 이라는 책을 통해 학습하였습니다.
교재 구매 : https://www.yes24.com/Product/Goods/83568594
Django 한 그릇 뚝딱 - 예스24
실전 프로젝트로 하나씩 직접 만들며 배우는 웹 프로그래밍이 책에서는 빠른 생산성과 쉬운 내용으로 주목받고 있는 언어인 파이썬 그리고 파이썬을 기반으로 한 웹 프레임워크인 Django를 ToDoLis
www.yes24.com
Edit Log
▣ 프로젝트 진행할 디렉토리에 가상 환경 설치 후 가상 환경에 접속하고 아래 라이브러리들을 설치 해준다.
# install list : pip install django openpyxl pandas jinja2
Seon Jae Choi@DESKTOP-1HGASOH MINGW64 ~/TIL/ExcelCalculator (main)
$ virtualenv venv
Seon Jae Choi@DESKTOP-1HGASOH MINGW64 ~/TIL/ExcelCalculator (main)
$ source venv/Scripts/activate
Seon Jae Choi@DESKTOP-1HGASOH MINGW64 ~/TIL/ExcelCalculator (main)
$ pip install -r requirements.txt
Seon Jae Choi@DESKTOP-1HGASOH MINGW64 ~/TIL/ExcelCalculator (main)
$ django-admin startproject ExcelCalculate
(venv)
Seon Jae Choi@DESKTOP-1HGASOH MINGW64 ~/TIL/ExcelCalculator (main)
$ ls
ExcelCalculate/ README.md requirements.txt venv/
Seon Jae Choi@DESKTOP-1HGASOH MINGW64 ~/TIL/ExcelCalculator/ExcelCalculate (main)
$ python manage.py startapp main
(venv)
Seon Jae Choi@DESKTOP-1HGASOH MINGW64 ~/TIL/ExcelCalculator/ExcelCalculate (main)
$ ls
ExcelCalculate/ main/ manage.py*
(venv)
Seon Jae Choi@DESKTOP-1HGASOH MINGW64 ~/TIL/ExcelCalculator/ExcelCalculate (main)
$ python manage.py startapp sendEmail
(venv)
Seon Jae Choi@DESKTOP-1HGASOH MINGW64 ~/TIL/ExcelCalculator/ExcelCalculate (main)
$ python manage.py startapp calculate
(venv)
▣ 명령어 리뷰
# install list : pip install django openpyxl pandas jinja2
in TERMINAL 명령어 리뷰
$ virtualenv venv # 가상환경 구축
$ source venv/Scripts/activate # 가상환경 활성화
$ pip install -r requirements.txt # 해당 텍스트 파일에 있는 라이브러리 목록 인스톨
# 가상환경 상태 ing
$ django-admin startproject ExcelCalculate
$ python manage.py startapp main
$ python manage.py startapp sendEmail
$ python manage.py startapp calculate
# django-admin : Django 프로젝트를 관리하는 명령어 도구 실행 명령어
# startproject : 새로운 Django 프로젝트 생성 명령어
# ExcelCalculate, main, sendEmail, calculate : 생성할 Django 프로젝트 또는 어플 이름
# startapp : 새로운 어플 생성 명령어
▣ App Intergration
# App Integration
# ExcelCalculate > ExcelCalculate > settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'calculate',
'main',
'sendEmail',
]
# Email Settings
# ExcelCalculate > ExcelCalculate > settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_USE_TLS = True
EMAIL_PORT = 587
EMAIL_HOST_USER = 'example@example.com' # 변수
EMAIL_HOST_PASSWORD = 'examplepw' # 변수
▣ model.py 에서 User 정의하기
# ExcelCalculate > main > models.py
# Create your models here.
from django.db import models
class User(models.Model):
user_name = models.CharField(max_length=20)
user_email = models.EmailField(unique=True)
user_password = models.CharField(max_length=100)
user_validate = models.BooleanField(default=False)
▣ model.py 에서 모델 정의 해준 후 아래 와 같이 코드로 DB를 업데이트 과정
Seon Jae Choi@DESKTOP-1HGASOH MINGW64 ~/TIL/ExcelCalculator/ExcelCalculate (main)
$ python manage.py makemigrations
Seon Jae Choi@DESKTOP-1HGASOH MINGW64 ~/TIL/ExcelCalculator/ExcelCalculate (main)
$ python manage.py migrate
Seon Jae Choi@DESKTOP-1HGASOH MINGW64 ~/TIL/ExcelCalculator/ExcelCalculate (main)
python manage.py createsuperuser
Username (leave blank to use 'your_username'): admin
Email address: admin@example.com
Password: ********
Password (again): ********
Superuser created successfully.
# 마이그레이션 파일은 Django에서 데이터베이스 스키마 변경을 추적하는 파일임.
# 데이터베이스 스키마란 데이터베이스에 저장되는 테이블과 필드들의 구조를 정의하는 것을 의미함.
▣ In main > index.html 코드 中
▣ calculate
index.html 이라는 메인 화면 파일로 form action="calculate/" 를
통해 엑셀 파일 업로드 및 제출하기 시메인경로/calculate/ 로 이동하기
▣ logout
a hred = "logout" 으로 로그아웃 시 logout 경로로 이동
<title>Excel Calculate</title>
</head>
<body> <--! 메인 경로임 -->
<div class="container">
<div class="header">
<div class="page-header">
<h1>Excel Calculate <small>with Django</small><a href="logout" class="btn btn-danger btn-xs" style="margin-left: 30px">로그아웃</a></h1>
</div>
</div>
<div class="content"> <--! content 함수로 엑셀 파일을 인풋 받으면 calculate/ 경로로 이동 -->
<div class="fileInputDiv">
<form action="calculate/" method="POST" enctype="multipart/form-data">{% csrf_token %}
<div class="input-group">
하단 버튼을 통해 파일을 업로드 해주세요.(.xls 확장자의 파일만 가능합니다.)<br>
<input id="fileInput" name="fileInput" type="file" class="form-control">
<input type="submit" class="btn btn-success btn-lg" value="파일 제출">
</div>
</form>
</div>
</div>
<div class="panel-footer">
실전예제로 배우는 Django. Project3-ExcelCalculate
</div>
</div>
</body>
</html>
▣ In main > urls.py 中
# ExcelCalculate > main > urls.py
from django.urls import path
from . import views
urlpatterns = [
# EX) path( "경로", "함수명", "메소드")
# 경로 = http://localhost:8888/경로~
# 메소드 = url을 식별하기 위해 설정
path('', views.index, name="main_index"),
path('signup', views.signup, name="main_signup"),
path('signup/join', views.join, name="main_join"),
path('signin', views.signin, name="main_signin"),
path('signin/login', views.login, name="main_login"),
path('loginFail', views.loginFail, name="main_loginFail"),
path('verifyCode', views.verifyCode, name="main_verifyCode"),
path('verify', views.verify, name="main_verify"),
path('result', views.result, name="main_result"),
path('logout', views.logout, name="main_logout")
]
▣ In main > views.py 코드 中
▣ 각종 함수 기재 내용은 주석 참고
# ExcelCalculate > main > views.py
# Create your views here.
from django.shortcuts import render, redirect
from django.http import HttpResponse
from .models import *
from random import *
from sendEmail.views import *
def index(request):
# 로그인된 사용자만 접근
# 조건문 : 세션 정보의 사용자가 존재하면 메인 화면으로 출력
# 만약, 사용자의 정보가 세션에 없다면 로그인 화면으로 출력
if 'user_name' in request.session.keys():
return render(request, 'main/index.html') # 사용자의 세션 정보가 담겨져 있는 상태의 index.html
else:
return redirect('main_signin')
# return render(request, "main/index.html") # 아무런 세션 정보가 없는 상태의 index.html
def signup(request):
return render(request, "main/signup.html")
def join(request):
name = request.POST['signupName']
email = request.POST['signupEmail']
pw = request.POST['signupPW']
user = User(user_name = name, user_email = email, user_password = pw)
user.save()
# 유저의 입력 값들을 받아와서 저장 = html input 되는 name 값
# 인증코드 하나 생성
code = randint(1000, 9000)
print("인증코드 생성 ---------------", code) # 서버가 보낸 코드, 쿠키와 세션
response = redirect('main_verifyCode') # 응답을 객체로 저장한다!
response.set_cookie('code', code) # 인증코드
response.set_cookie('user_id', user.id)
print("응답 객체 완성 ---------------", response)
# 이메일 발송 하는 함수 만들기
send_result = send(email, code)
if send_result:
print("Main > views.py > 이메일 발송 중 완료된 것 같음...")
return response
else:
return HttpResponse("이메일 발송 실패!")
def signin(request):
return render(request, "main/signin.html")
def login(request):
# 로그인된 사용자만 이용할 수 있도록 구현 // 독립된 로그인 정보를 보이게끔 함
# 이 때, 현재 사용자가 로그인된 사용자인지 판단하기 위해 세션 사용 FROM(verify 함수에서 만든 세션)
# 세션 처리 진행
print("---------------접근 성공---------------")
loginEmail = request.POST['loginEmail']
loginPW = request.POST['loginPW']
# 로그인 시도 시, User 모델에 접근 후, 사용자가 입력한 이메일로 탐색 시도
# 회원 미가입 또는 비번 틀릴 경우, 에러가 발생하고 이상한 페이지 나옴
# 예외 처리
try :
user = User.objects.get(user_email = loginEmail)
except:
return redirect("main_loginFail")
if user.user_password == loginPW: # 회원 가입 시 입력한 패스워드와 로그인 패스워드가 동일하면
print("---------------매칭 성공---------------")
request.session['user_name'] = user.user_name # 사용자 회원가입 시 입력한 정보
request.session['user_email'] = user.user_email # 사용자 회원가입 시 입력한 정보
return redirect('main_index') # return none 으로 확인 가능
else:
# 로그인 실패, 정보가 다름
print("---------------매칭 실패---------------")
return redirect("main_loginFail")
def loginFail(request):
return render(request, 'main/loginFail.html')
def verifyCode(request):
return render(request, "main/verifyCode.html")
def verify(request):
# 사용자가 입력한 code 값을 받아야 함
user_code = request.POST['verifyCode']
# 쿠키에 저장되어 있는 code값을 가져온다. (join 함수 확인)
cookit_code = request.COOKIES.get('code')
print("코드 확인 : ", user_code, cookit_code)
if user_code == cookit_code:
user = User.objects.get(id=request.COOKIES.get('user_id')) # SELECT FROM WHERE id = cookie_id 데이터를 가져오는 것
user.user_validate = 1 # True 1 False 0
user.save()
print("DB에 user_validate 업데이트 ----------")
response = redirect('main_index')
# 저장되어 있는 쿠키를 삭제
response.delete_cookie('code')
response.delete_cookie('user_id')
# response.set_cookie('user', user)
# 사용자 정보를 세션에 저장
request.session['user_name'] = user.user_name ## 로그인 화면 구현
request.session['user_email'] = user.user_email ## 로그인 화면 구현
return response
else:
return redirect('main_verifyCode') # verifyCode 인증화면으로 돌리기
# return redirect('main_index') # 인증 완료 시 메인 페이지로 보내줌
def result(request):
if 'user_name' in request.session.keys():
# 현재 상황은 user_name, user_email 정보가 존재함
# 현재 calculate 두개의 정보가 들어 있음
content = {}
# 새로운 객체에 저장
content['grade_calculate_dic'] = request.session['grade_calculate_dic']
content['email_domain_dic'] = request.session['email_domain_dic']
content['grade_calculate_pd_dic'] = request.session['grade_calculate_pd_dic']
print(content['grade_calculate_pd_dic'])
# 기존 세션 삭제
del request.session['grade_calculate_dic']
del request.session['email_domain_dic']
del request.session['grade_calculate_pd_dic']
return render(request, "main/result.html", content)
else:
return redirect('main_signin')
def logout(request):
# 로그아웃의 개념 : 세션 정보를 삭제하는 것
# 파이썬에서 객체를 지울 때
del request.session['user_name']
del request.session['user_email']
return redirect('main_signin')
▣ In sendEmail > views.py & urls.py & email_format.html
▣ 생성된 인증코드를 회원가입 유저에게 이메일을 송부하는 코드
# Create your views here.
# Excelcalculor > sendEmail > views.py
from django.shortcuts import render
from django.http import HttpResponse
from django.core.mail import send_mail, EmailMessage
from django.template.loader import render_to_string
# 인증코드 발송은 에러 발생 가능성 존재
# try-except 구문 사용 : Google 이용 <--- 개발자가 통제할 수 있는 영역이 아님
# main > join 함수 내부에서 사용 될 것
# verifyCode : 인증코드
def send(receiverEmail, verifyCode):
print(receiverEmail, verifyCode)
try:
content = {'verifyCode': verifyCode}
msg_html = render_to_string("sendEmail/email_format.html", content)
msg = EmailMessage(subject="[멀티캠퍼스]인증 코드 발송 메일",
body=msg_html,
from_email="cafmaka@gmail.com",
bcc=[receiverEmail])
msg.content_subtype="html"
msg.send() # msg 객체 안에 send 라는 매소드 정의 되있음
print("sendEmail > views.py > send 함수 임무 완수 ----------")
return True
except:
print("sendEmail > views.py > send 함수 임무 실패 원인 파악하세요. ----------")
return False
# return HttpResponse("sendEmail, send function!")
from django.urls import path
from . import views
urlpatterns = [
path('send', views.send, name="email_send")
]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<h1>ExcelCalculate 회원 가입</h1>
<p>다음의 인증 코드를 입력해 주세요</p>
<h2> {{ verifyCode }} </h2> {# verifyCode : 해당 변수값 가져오기 #}
</body>
</html>
▣ In calculate > urls.py & views.py
# ExCelCalculate > calculate > urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.calculate, name="calculate_do"),
]
# Excalculator > calculate > views.py
from django.shortcuts import render, redirect
from django.http import HttpResponse
import pandas as pd
# Create your views here.
def calculate(request):
file = request.FILES['fileInput']
print("# 사용자가 등록한 파일의 이름 : ", file)
df = pd.read_excel(file, sheet_name="Sheet1", header=0)
print(df.head())
# 과제 위 코드가 맘에 안듬 ---> pandas 문법으로 변환해서 2개 Tasks를 완료
grade_df = df.groupby('grade')['value'].agg(["min", "max", "mean"]).reset_index().rename(columns = {"mean" : "avg"}).reset_index()
grade_df = grade_df.astype({"min" : "int", "max" : "int", "avg" : "float"})
grade_df = grade_df.style.hide(axis="index").set_table_attributes('class="table table-dark"')
# print(grade_df)
# print("")
grade_calculate_dic_pd_to_session = {'grade_df' : grade_df.to_html(justify="center")}
request.session['grade_calculate_pd_dic'] = grade_calculate_dic_pd_to_session
df['domain'] = df['email'].apply(lambda x : x.split("@")[1])
email_df = df.groupby('domain')['value'].agg("count").sort_values(ascending=False).reset_index()
print(email_df)
# grade별 value 리스트 만들기
grade_dic = {} # 빈 딕셔너리 생성
total_row_num = len(df.index)
# print(total_row_num)
for i in range(total_row_num):
data = df.loc[i, :]
if not data.grade in grade_dic.keys():
grade_dic[data.grade] = [data.value]
else:
grade_dic[data.grade].append(data.value)
# print(grade_dic)
grade_calculate_dic = {}
for key in grade_dic.keys():
grade_calculate_dic[key] = {}
grade_calculate_dic[key]['min'] = min(grade_dic[key]) # 각 grade별 최소값 ex) grade 1의 최소값
grade_calculate_dic[key]['max'] = max(grade_dic[key]) # 각 grade별 최대값
grade_calculate_dic[key]['avg'] = float(sum(grade_dic[key])) / len(grade_dic[key])
# print(grade_calculate_dic)
# 결과 출력
grade_list = list(grade_calculate_dic.keys())
# print("")
# print(grade_list)
grade_list.sort()
# print(grade_list)
for key in grade_list:
print("# grade: ", key)
print("min:", grade_calculate_dic[key]['min'], end="")
print("/ max:", grade_calculate_dic[key]['max'], end="")
print("/ avg:", grade_calculate_dic[key]['avg'], end="\n\n")
# 이메일 주소 도메인별 인원 구하기
email_domain_dic = {}
for i in range(total_row_num):
data = df.loc[i, :]
email_domain = data['email'].split("@")[1]
print(data.email)
if not email_domain in email_domain_dic.keys():
email_domain_dic[email_domain] = 1 # 새로운 도메인이 나타나면 1 숫자 부여
else:
email_domain_dic[email_domain] += 1 # 기존 도메인이 또 나타나면 +1 숫자 부여 (Count)
print("## Email 도메인별 사용 인원")
for key in email_domain_dic.keys():
print("#", key, ": ", email_domain_dic[key], "명")
# pandas django 데이터 타입이 조금 다름
# pandas의 데이터 타입 ==> 파이선 기본 데이터 타입으로 변환
grade_calculate_dic_to_session = {}
for key in grade_list:
grade_calculate_dic_to_session[int(key)] = {}
grade_calculate_dic_to_session[int(key)]['min'] = float(grade_calculate_dic[key]['min']) # 정수형 자료로 변환
grade_calculate_dic_to_session[int(key)]['max'] = float(grade_calculate_dic[key]['max']) # 정수형 자료로 변환
grade_calculate_dic_to_session[int(key)]['avg'] = float(grade_calculate_dic[key]['avg']) # 실수형 자료로 변환
request.session['grade_calculate_dic'] = grade_calculate_dic_to_session
request.session['email_domain_dic'] = email_domain_dic
return redirect("/result")
▣ DB 제거 시
rm -rf db.sqlite3
## 이메일 인증으로 회원가입 프로세스
-1) 회원 가입 화면에서 개인정보 입력 후, 회원 가입하기 버튼 클릭
-2) 해당 입력한 정보로 USER 데이터 모델에 추가 및 DB 저장, 인증 ## 금일 여기까지
-3) 입력한 이메일로 인증 코드 발송
-4) verifyCode 화면으로 전환
-5) 사용자가 인증코드 입력, 인증하기 버튼 클릭하면 일치 시 성공, 일치하지 않을 시 실패
-6) 성공 시 회원의 인증이 완료된 회원 등록 / 회원정보 삭제
인증코드를 무작위로 생성 후 --->
회원가입 본인이 맞는 지 인증코드, 이름 , 이메일, 기타 정보도 같이 보내야 함
사용자에게 보냄 ---> (서버가)사용자에게 보낸 코드 == 사용자 입력 코드가 동일 해야됨
일치 시 회원가입 성공
아닐 시 계속아님 !!