상세 컨텐츠

본문 제목

django 실습 ExcelCalculator 리뷰

django

by seonjaechoi 2023. 7. 31. 13:42

본문

반응형

본 실습 리뷰는 아래 교재인 django 한그릇뚝딱 이라는 책을 통해 학습하였습니다.

교재 구매 : https://www.yes24.com/Product/Goods/83568594

 

Django 한 그릇 뚝딱 - 예스24

실전 프로젝트로 하나씩 직접 만들며 배우는 웹 프로그래밍이 책에서는 빠른 생산성과 쉬운 내용으로 주목받고 있는 언어인 파이썬 그리고 파이썬을 기반으로 한 웹 프레임워크인 Django를 ToDoLis

www.yes24.com


더보기

Edit Log

  • 2023-08-03 수업 리뷰 추가

▣ 프로젝트 진행할 디렉토리에 가상 환경 설치 후 가상 환경에 접속하고 아래 라이브러리들을 설치 해준다.

# 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 정의하기

  • 코드 블럭 최종 경로인 models.py 에서 class 함수를 통해 회원 가입하는 유저를 정의한다.
  • email 은 중복되지 않게끔 unique = True 옵션을 준다.
# 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를 업데이트 과정

  • python manage.py = 마이그래이션 파일을 생성
  • python 
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 디렉토리 교재 순서와 상관없이 코드 리뷰하기

▣ 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 디렉토리 교재 순서와 상관없이 코드 리뷰하기

▣ 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  디렉토리 교재 순서와 상관없이 코드 리뷰하기

▣ 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) 성공 시 회원의 인증이 완료된 회원 등록 / 회원정보 삭제

 

인증코드를 무작위로 생성 후 --->

회원가입 본인이 맞는 지 인증코드, 이름 , 이메일, 기타 정보도 같이 보내야 함

사용자에게 보냄 ---> (서버가)사용자에게 보낸 코드 == 사용자 입력 코드가 동일 해야됨

일치 시 회원가입 성공

아닐 시 계속아님 !!