본문 바로가기
Django

[Django] 엑셀 파일 읽어서 DB에 넣어버리기

by windy7271 2024. 11. 12.
728x90
반응형

 

지난 번에 로또번호 엑셀로 받아왔잖아요.

 

그거 근데 너무 빠르게 읽어오길래 놀라가지고 DB 넣는데 얼마나 걸리는지좀 봐보려고요

 

# forms.py
from django import forms
from .models import ExcelFile

class ExcelFileForm(forms.ModelForm):
    class Meta:
        model = ExcelFile
        fields = ['file']

 

일단 엑셀 받을 폼이고요

 

from django.db import models

class ExcelFile(models.Model):
    file = models.FileField(upload_to='excel_files/')

    class Meta:
        db_table = 'lotto'

class Lotto(models.Model):
    uploaded_at = models.DateTimeField(auto_now_add=True)

    lotto_id = models.IntegerField(null=True)  # 각 로또의 ID
    number_one = models.IntegerField()  # 첫 번째 번호
    number_two = models.IntegerField()  # 두 번째 번호
    number_three = models.IntegerField()  # 세 번째 번호
    number_four = models.IntegerField()  # 네 번째 번호
    number_five = models.IntegerField()  # 다섯 번째 번호
    number_six = models.IntegerField()  # 여섯 번째 번호

    def __str__(self):
        return str(self.lotto_id)

    class Meta:
        db_table = 'lottodata'

 

이것은 스프링으로 치면 객체 만드는 것으로 이해를 했습니다.

 

아 디장고는 처음에 db가 sqlite3 입니다.

이거를 mariadb로 바꿔주셔야 해요

 

setting.py 에서요

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  # MySQL 사용
        'NAME': '',          # 데이터베이스 이름
        'USER': '',               # 데이터베이스 사용자
        'PASSWORD': '',           # 데이터베이스 비밀번호
        'HOST': 'localhost',                   # 데이터베이스 호스트 (로컬이면 'localhost')
        'PORT': '3306',                        # MySQL 기본 포트
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'"
        },
    }
}

 

 

참고로 로또 엑셀 데이터가 이렇게 되어있어요

num은 회차입니다. 보너스 번호는 뺐어요

 

 

방법 1. objects.bulk_create 방식

from django.shortcuts import render, redirect
import pandas as pd
import time
from .forms import ExcelFileForm
from .models import  Lotto

from django.db import transaction


def upload_excel(request):
    if request.method == 'POST':
        form = ExcelFileForm(request.POST, request.FILES)
        if form.is_valid():
            # 엑셀 파일 읽기
            excel = pd.read_excel(request.FILES['file'])
            print(excel.head())

            # 각 행을 모델 인스턴스로 변환
            start_time = time.time() * 1000  # 밀리세컨드 단위
            records = [
                Lotto(
                    lotto_id=row['num'],
                    number_one=row['one'],
                    number_two=row['two'],
                    number_three=row['three'],
                    number_four=row['four'],
                    number_five=row['five'],
                    number_six=row['six']
                )
                for index, row in excel.iterrows()
            ]
            Lotto.objects.bulk_create(records)  # 데이터를 한 번에 삽입
            total_records = Lotto.objects.count()
            print(f"total count: {total_records}")

            end_time = time.time() * 1000
            time_taken = end_time - start_time
            print(f"Time taken: {time_taken} milliseconds")
            return render(request, 'upload_excel.html', {'form': form, 'data': excel.to_html()})
    else:
        form = ExcelFileForm()
    return render(request, 'upload_excel.html', {'form': form})

 

  • 성능 향상: 여러 데이터를 개별적으로 삽입하는 것보다 번에 삽입하면 데이터베이스와의 통신 횟수가 줄어들어 성능이 크게 향상. 특히 대량의 데이터를 삽입할 효과적.
  • 트랜잭션 관리: bulk_create 메소드는 기본적으로 하나의 트랜잭션 내에서 모든 삽입 작업을 처리하므로, 삽입 도중 오류가 발생할 경우 모든 변경 사항을 롤백할 있어 데이터 일관성이 유지.
  • 코드 가독성: 여러 개의 개별 삽입 문을 작성하는 것보다 코드가 간결하고 가독성이 높아짐.
  • 오버헤드 감소: 개별 삽입마다 발생하는 오버헤드를 줄여주기 때문에 전체적인 효율성이 증가

 

 

대충 이정도 나와요 1000개 에 90ms ~ 150ms 정도 나오는것 같네요 

 

그 다음은 원초적인 방법이 for문으로 집어넣어볼게요

 

from django.shortcuts import render
import pandas as pd
import time
from .forms import ExcelFileForm
from .models import Lotto


def upload_excel(request):
    if request.method == 'POST':
        form = ExcelFileForm(request.POST, request.FILES)
        if form.is_valid():
            # 엑셀 파일 읽기
            excel = pd.read_excel(request.FILES['file'])

            # 각 행을 모델 인스턴스로 변환하여 한 줄씩 DB에 저장
            start_time = time.time() * 1000  # 밀리세컨드 단위

            # 한 줄씩 저장
            for index, row in excel.iterrows():
                # Lotto 모델 인스턴스 생성
                lotto = Lotto(
                    lotto_id=row['num'],
                    number_one=row['one'],
                    number_two=row['two'],
                    number_three=row['three'],
                    number_four=row['four'],
                    number_five=row['five'],
                    number_six=row['six']
                )
                # DB에 저장
                lotto.save()

            total_records = Lotto.objects.count()
            print(f"total count: {total_records}")

            end_time = time.time() * 1000
            time_taken = end_time - start_time
            print(f"Time taken: {time_taken} milliseconds")

            return render(request, 'upload_excel.html', {'form': form, 'data': excel.to_html()})
    else:
        form = ExcelFileForm()
    return render(request, 'upload_excel.html', {'form': form})

 

한 줄씩 읽어와서 DB에 저장하는 식이에요

이렇게 되면

 

1000개당 360 ~ 620 정도 나옵니다. 상당히 느려요

 

하지만 빠르다고 좋은게 아니지요 

bulkcreate 는 유효성 검사를 수행하지 않고, pre_save, post_save 등의 후크롤 호출하지 않아 저장 전후 자동작업을 할 수 없습니다.

 

소규모 에선 save() 

대규모에서는 bulk_create() 를 선호한다.

반응형

댓글