취미삼아 배우는 프로그래밍

Django로 개인 업무(ERP) 홈페이지 만들기-5 본문

파이썬(장고)

Django로 개인 업무(ERP) 홈페이지 만들기-5

Nadure 2020. 11. 14. 05:45

테스트 코드를 작성해봤다.

순서를 섞는 알고리즘에 관해 아이디어만 있을 뿐 구체적인 방법이 떠올라지지가 않았기 때문이다.

 테스트코드를 먼저 작성한것은 아주 옳은 선택이었다. 구체적인 방법 및 한계등에 직면하게 되고 내가 구상한 알고리즘에 대한 신뢰도가 깔렸다.

전체코드

from django.test import TestCase
from standardwall.models import *
import random

'''
filter > 
    # base_data__is_ordered_on = True
    order_by : rank_number

    Step1 Make Dummy Data
    Step2 Randomly 'is_complete = True' 
    Step3 Filter(is_complete = False, orderby rank_number)
    Step4 Rearrange

'''

'''
Result : 
    LIMIT > 
        almost 10000 rows
        all listing data out > all listing(changed) data input > change rank
    
'''

class RankModuleTest(TestCase):
    def setUp(self):
        # Setup run before every test method.
        company_name_base = "업체명_"
        location_name_base = "현장명_"
        bulk_list = []
        count = 1000
        rand_int_count = count // 25
        batch_size = 500

        self.batch_size = 500
        self.count = count
        

        # BaseData Setup
        for i in range(count):
            company_name = company_name_base + str(i)
            location_name = location_name_base + str(i)
            bulk_list.append(
                BaseLocationData(
                    company=company_name, 
                    location=location_name
                    )
                )               
        BaseLocationData.objects.bulk_create(bulk_list)

        # LocationData Setup
        bulk_list = []
        for i in range(1,count+1):
            bulk_list.append(
                LocationData(
                    base_id=i,
                    district=i,
                    is_ordered_on=False,
                )
            )
        LocationData.objects.bulk_create(bulk_list)

        # IntegratedInfo Setup
        bulk_list = []
        for i in range(1, count+1):
            bulk_list.append(
                IntegratedInfo(
                    base_data_id=i,
                    is_completed=False,
                    is_order_printed=False,
                    rank_number=i,
                )
            )
        IntegratedInfo.objects.bulk_create(bulk_list)

        # Random id 리스트 작성, 
        randomlist = random.sample(range(1,count), rand_int_count)
        objs = IntegratedInfo.objects.filter(pk__in=randomlist)  # randomlist 이내의 아이디 만 필터링

        # Random id 리스트 내의 is_completed 필드값들을 True로 변경
        for obj in objs:
            obj.is_completed = True # 값 변경
        IntegratedInfo.objects.bulk_update(
            objs, ['is_completed'], batch_size=batch_size) # bulk_update

        self.assertEqual(
            IntegratedInfo.objects\
                .filter(is_completed=True).count(), rand_int_count)
        self.assertEqual(
            IntegratedInfo.objects\
            .get(id=randomlist[0]).is_completed, True)
        



    def tearDown(self):
        # Clean up run after every test method.
        pass

    def test_order_shuffle_and_validation(self):
        # id_list를 리스트 형식으로 얻어낸다.
        qs = IntegratedInfo.objects.filter(is_completed=False).order_by('rank_number')
        qs_id_list = list(qs.values_list('id', flat=True))

        random.shuffle(qs_id_list) # rank_number를 섞고
        
        # 섞은 rank_number를 업데이트
        objs = IntegratedInfo.objects.filter(pk__in=qs_id_list)
        for obj, rank_val in zip(objs, qs_id_list):
            obj.rank_number = rank_val
        IntegratedInfo.objects.bulk_update(objs, ['rank_number']) # bulk_update
        
        # 섞은 id리스트와 처음의 id리스트가 같은지 확인
        after_qs = IntegratedInfo.objects.filter(is_completed=False).order_by('rank_number')
        after_qs_id_list = list(after_qs.values_list('id', flat=True))

        self.assertNotEqual(qs_id_list, after_qs_id_list)
        
        
    # For Data Validation
    def test_BaseLocationDataExist(self):
        self.assertTrue(BaseLocationData.objects.get(id=1).id == 1)
        self.assertTrue(BaseLocationData.objects.get(id=self.count).id == self.count)
        # self.assertFalse(False)

    def test_LocationDataExist(self):
        self.assertTrue(LocationData.objects.get(id=1).id == 1)
        self.assertTrue(LocationData.objects.get(id=self.count).id == self.count)

    def test_IntegratedInfo(self):
        self.assertTrue(IntegratedInfo.objects.get(id=1).id == 1)
        self.assertTrue(IntegratedInfo.objects.get(id=self.count).id == self.count)

 

부분부분 설명하자면,

1. base 값 설정

초기 값 설정

count 변수를 통해 실험적 데이터의 크기를 컨트롤 하려한다.

 

2.초기 값 설정

BaseLocationData 모델의 더미데이터를 만든다.

 

LocationData 더미데이터 또한 만든다.

 

IntegratedInfo 더미데이터를 연달아 만들어준다.

 

주로 다룰 모델은 IntegratedInfo 모델이다.

 

이제 randomlist를 작성해주고 이 값들만 필터링한다.

 

랜덤한 값들을 id로 잡고 is_completed 필드를 True로 변경해준뒤, bulk_update를 한다.

 

is_completed=True로 필터링하고 이 갯수와 rand_int_count 숫자가 일치하는지 확인한다.

 

randomlist 에서 하나를 꺼내어 is_completed 값이 True로 됐는지를 점검한다.
최종적으로 데이터들이 잘 생성됐는지 확인한다.

 

2. Rank 셔플

 

  1. is_completed=False 조건으로 필터링하고, 이 쿼리셋들에 대해서 id값들을 리스트로 얻어낸다
  2. 이 아이디 리스트 들을 랜덤으로 섞고,
  3. 섞은 id리스트는 zip을 이용해서 나란히 값을 입력하고 업데이트 한다.
  4. 맨 처음 쿼리셋 아이디 리스트와 나중의 쿼리셋 아이디 리스트들이 동일한지 비교한다.

 

ㅡㅡㅡ 테스트 결과

테스트 코드 작성하면서 좋은점

  • 테스트코드를 통해 생각만큼, 딱 구상한대로 코드를 짤 수 있었다. 맘에듬.
  • 이리저리 값을 바꾸다가 한계점에 대해 생각하게 된다.
  • 따지고 보면 rank_number 값은 겹치지 않는 여전히 유니크한 값이다.

 

테스트 코드 작성하면서 나쁜점

  • 분명 삽질을 줄여주는 거긴하지만 똑같은 코드를 두 번 작성해야한다는건 마음아픈 일이다.
  • 중간에 또 다른 뭔가가 껴서 로직이 바뀌면 테스트코드를 쓸때쯤에 또 만져줘야한다.

 

이거저거 만지면서 느낀 이 알고리즘의 한계점

  • 쿼리셋이 만 개쯤 되면 너무 많다고 안된다.(만 개 쯤까지 될 일은 없다. 끽해야 40~60개)
  • 순서가 바뀌면 그 바뀐 순서에 대해서 모든 데이터 값이 넘어와야만 한다.

 

만 개가 넘으면 도로 뱉어버린다.

 

 

Comments