이미 DB에 저장되어있는 오브젝트 리스트를 업데이트 하기위해 오브젝트 인스턴스의 속성마다 값을 설정해줘야하는 것이 번거롭다고 생각이 들었다. 팀장님께 문의한 결과 setattr를 이용하여 손쉽게 리팩토링 할 수 있었다.
data_no
과name
필드를 가진 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" } ]
여기서 각각의 데이터를 다음과 같이 한번에 수정하고 싶다고 가정하자
[ { "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 모델의 필드가
name
과data_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
를 이용해서 인스턴스의 해당 필드 값을 변경할 수 있다!
다만 해당 로직을 사용하는데 있어서 주의할 점이 몇가지 있따.
data_obj.__dict__.keys()
에는id
와 같이 내가 정의하지 않은 필드도 포함되니 조건문으로 걸러줘야한다.
- 위 예제에서는 request_data로 받은
data_info_list
의 값을 그대로 인스턴스에 넣어줬지만 만약 가공을 해야하는 경우 (datatime처럼) 해당 작업을 고려해야한다.