[230221 - TIL] setattr를 이용한 데이터 업데이트

Dongwoo Kim·2023년 2월 22일
0

TIL / WIL

목록 보기
86/113

1. 개요

이미 DB에 저장되어있는 오브젝트 리스트를 업데이트 하기위해 오브젝트 인스턴스의 속성마다 값을 설정해줘야하는 것이 번거롭다고 생각이 들었다. 팀장님께 문의한 결과 setattr를 이용하여 손쉽게 리팩토링 할 수 있었다.


2. 기존 데이터

data_noname 필드를 가진 Data 모델이 있다고 가정하자.
현재 Data를 조회하면 다음과 같다.

 [
    {
        "id": 1,
        "data_no": 1,
        "name": "1번 data"
    },
    {
        "id": 2,
        "data_no": 2,
        "name": "2번 data"
    },
    {
        "id": 3,
        "data_no": 3,
        "name": "3번 data"
    }
]

2. 데이터 수정

여기서 각각의 데이터를 다음과 같이 한번에 수정하고 싶다고 가정하자

[
    {   
        "id": 1,
        "data_no": 1,
        "name": "1번 data update"
    }, {
        "id": 2,
        "data_no": 3,
        "name": "3번으로 바뀐 2번 data"
    }, {
        "id": 3,
        "data_no": 4,
        "name": "4번으로 바뀐 3번 data"
    }
]

1) 기존 로직

내가 작성했던 로직은 다음과 같다.

        data_info_list = request.data

        data_info_dict = {}
        data_id_list = []

        for data_info in data_info_list:
            data_info_dict[data_info["id"]] = data_info
            data_id_list.append(data_info["id"])

        data_obj_list = DataModel.objects.filter(id__in=data_id_list)
        data_obj_dict = {data_obj.id : data_obj for data_obj in data_obj_list}

        for data_info in data_info_list:
            data_obj_dict[data_info["id"]].name = data_info["name"]
            data_obj_dict[data_info["id"]].data_no = data_info["data_no"]

        DataModel.objects.bulk_update(data_obj_list, fields=["name", "data_no"])

이전에 배운 bulk_update 를 통해 여러 데이터를 한번에 멋지게 업데이트하긴 했지만 아래부분처럼 data 인스턴스의 필드에 일일히 접근해서 값을 변경해줘야한다.

        for data_info in data_info_list:
            data_obj_dict[data_info["id"]].name = data_info["name"]
            data_obj_dict[data_info["id"]].data_no = data_info["data_no"]

지금은 Data 모델의 필드가 namedata_no 2개라서 괜찮아보이지만 필드가 많아지면 많아질수록 매우 번거로워질 것이다.

2) setattr를 이용한 리팩토링

setattr를 이용하면 필드 개수와 상관없이 쉽게 인스턴스의 필드값을 변경할 수 있다.

        data_info_list = request.data

        data_info_dict = {}
        data_id_list = []

        for data_info in data_info_list:
            data_info_dict[data_info["id"]] = data_info
            data_id_list.append(data_info["id"])
        
        data_obj_list = DataModel.objects.filter(id__in=data_id_list)

        bulk_update_fields = []
        for data_obj in data_obj_list:
            data_info = data_info_dict[data_obj.id]
           
            update_fields = data_info.keys()
            data_fields = data_obj.__dict__.keys()

            for data_field in data_fields:
                if data_field == "id":
                    continue

                if data_field not in update_fields:
                    continue

                setattr(data_obj, data_field, data_info[data_field])
                bulk_update_fields.append(data_field)

        DataModel.objects.bulk_update(data_obj_list, fields=bulk_update_fields)

data_fields = data_obj.__dict__.keys() 으로 인스턴스의 필드들을 리스트로 가져올 수 있고 setattr를 이용해서 인스턴스의 해당 필드 값을 변경할 수 있다!


3. 주의사항

다만 해당 로직을 사용하는데 있어서 주의할 점이 몇가지 있따.

  • data_obj.__dict__.keys()에는 id 와 같이 내가 정의하지 않은 필드도 포함되니 조건문으로 걸러줘야한다.

  • 위 예제에서는 request_data로 받은 data_info_list의 값을 그대로 인스턴스에 넣어줬지만 만약 가공을 해야하는 경우 (datatime처럼) 해당 작업을 고려해야한다.

4. 예제코드

profile
kimphysicsman

0개의 댓글