Djagno queery set 합치기, 코드 리펙토링

김하진·2022년 7월 13일
0

오늘 코드를 구현하고 뭔가 굉장히 마음에 들지 않아 정한이형과 같이 리펙토링을 했다!

우선

 worry_list = []
        for cate_get in range(1, 7):
            worry_gets = WorryBoardModel.objects.filter(category=cate_get).order_by(
                "-create_date"
            )[:3]
            for worry_get in worry_gets:
                cate = {
                    "worry_id": worry_get,
                    "category": worry_get.category,
                    "content": worry_get.content,
                }
                worry_list.append(cate)

위와 같은 코드이다.

이번 프로젝트 메인페이지에서는 카테고리별(총6개) 에서 게시글을 3개씩 가져와야 하는데 이 쿼리셋을 합치는

코드를 짜는데 2중 for문을 쓰는것도 그렇고 너무 비효율적이고, 맘에 들지 않는 코드다.

 worry_list = WorryBoardModel.objects.none()
        for cate_get in WorryCategory.objects.all():
            worry_list = worry_list.union(WorryBoardModel.objects.filter(category=cate_get).order_by("-create_date")[:3])

저코드를 3줄로 줄였다.

굳이 2중for 문을 돌릴필요 없이 빈쿼리셋을 선언하고, 거기에 union 으로 합쳐주면 된다

장고 쿼리셋 합치기

qs1 = Toy.objects.filter(price__lte=10000) // 만원 이하인 장난감들
qs2 = Toy.objects.filter(price__gt=20000)  // 2만원이 넘는 장난감들

// 방법1
result_set = qs1 | qs2  // qs1과 qs2를 합친 결과

// 방법2
result_set2 = qs1.union(qs2)

이 쿼리셋을 합치기 위해 필요한 조건은 합치려고 하는 두 쿼리셋이 같은 필드를 갖고 있어야 한다는 점이다. 사실 쿼리셋을 합치기 위해선 같은 모델의 쿼리셋이라고 설명하는 블로그들이 많은데 엄밀히 말하면 같은 모델이라도 필드명과 타입이 똑같으면 쿼리셋을 합칠 수 있다는 것이다.

물론 필드가 다르더라도 ORM 작성시 annotate를 통해 필요한 컬럼을 붙여 최종적으로 queryset에 담겨있는 모델의 스키마가 같게 해주면 합쳐질 수 있다.

class Toy(models.Model):
    name = models.CharField(max_length=50, help_text='이름')
    price = models.IntegerField(help_text='가격')
    company = models.CharField(max_length=50, help_text='판매사')
    
class Toy2(models.Model):
    name = models.CharField(max_length=50, help_text='이름')
    price = models.IntegerField(help_text='가격')

위의 Toy와 Toy2 모델은 다른 모델이지만 다음과 같이 company 컬럼을 추가해 주어 쿼리셋을 만들어 주면 Toy 쿼리셋과 합칠 수 있다는 것이다.

Toy2.objects.annotate(company=Value('company2', output_field=CharField())

  • Django는 db접근의 효율을 높이기 위하여 코드를 읽는 순간 db에 접근하여 ORM의 결과를 수행한 결과를 가져오는 것이 아닌 필요시점에 접근하여 가져오게 된다. 위의 코드 대로 라면 qs1, qs2는 result에 합쳐지는 순간 쿼리를 해서 가져온다. 그 순간에 '|'연산자와 union의 동작 방식의 차이때문에 결과에도 차이가 생기게 된다.

serialzier 의 올바른 사용

letter_author = request.user
        title = request.data["title"]
        content = request.data["content"]
        worry_board_get = request.data["worry_board_id"]
        category = WorryBoardModel.objects.get(id=worry_board_get).category.id
        post_datas = {
            "category": category,
            "letter_author": letter_author.id,
            "title": title,
            "content": content,
            "worryboard": worry_board_get,
        }
        letterserialzier = LetterSerilaizer(data=post_datas)
        letterserialzier.is_valid(raise_exception=True)
        letterserialzier.save()

이렇게 하면, serialzier를 굳이 쓰는 이유도 없고, 쓸데 없는 코드도 너무 많다.


        worry_board_get = request.data['worry_board_id']
        request.data['letter_author'] = request.user.id
        request.data['category'] = WorryBoardModel.objects.get(id=worry_board_get).category.id
        letterserialzier = LetterSerilaizer(data=request.data)
        letterserialzier.is_valid(raise_exception=True)
        letterserialzier.save(worryboard=WorryBoardModel.objects.get(id=worry_board_get))

이렇게 간단하게 리펙토링 할 수 있다.

코드를 짤때 항상 간결하게, 누구나 보기 쉽게, 그리고 효율적으로 짜기.

profile
진킴

0개의 댓글