일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- optimization page
- orm 최적화
- 페이지 최적화
- 중량 관리
- QApplication
- django erp
- django drf
- ERP
- pip 오류
- test drive development
- 장고
- materialized
- django test
- uiload
- django rank
- query 최적화
- django role based
- Python
- pyside6
- qwindows.dll
- qpa_plugin
- channels
- pip 설치
- tensorflow
- 재고 관리
- 파이썬
- django
- Self ERP
- 장고로 ERP
- pyside6 ui
- Today
- Total
취미삼아 배우는 프로그래밍
페이지 최적화 예제(django queryset optimization example) 본문
겨우
하나의 뷰에 대한 최적화를 끝냈다.
쿼리식을 짜는데만 이틀이나 걸렸다(퇴근하고..)
이전 글인
ORM코드 최적화가 필요하다.
새벽에 파이썬 관련 카톡방에서 어느분이 쿼리가 한 번만 해야되는데 여러번 날라간다고 해서, 나도 한 번 debug toolbar를 깔아서 실행해봤다. 1. 2. 이건 48개 쿼리중에 36개가 중복.. 양호한가? 3. 이
nadure.tistory.com
에서
의 쿼리수는 대략
(내가 빨리 만드려고 너무 property를 남발한것 같다... 나중에 되서야 깨달았다. property하나를 호출할 때마다 쿼리 하나였다..ㅎㅎ,,)
그래서 이 페이지를 최적화 하는데만 2일정도 걸렸다.
뷰 코드
class ListDataView_v2(View):
# and ajax_call is need;
def get(self, request, coating_company_id, year, month):
date_obj = datetime.date(year, month, 1)
angle_list = ['ANGLE', 'ANGLE ', 'angle', 'Angle']
template_name = 'pages/view_list_v2.html'
end_day = calendar.monthrange(year, month)[1]
data_range_query = Q(worked_date__range = [f"{year}-{month}-01",
f"{year}-{month}-{end_day}"])
data_range_query_col = Q(target_date__range = [f"{year}-{month}-01",
f"{year}-{month}-{end_day}"])
coated_company = CoatCompanyModel.objects.get(id=coating_company_id).coated_company
try:
coll = CollectedFilesModel.objects \
.filter(data_range_query_col)\
.filter(target_coat_company_id=coating_company_id).get()
except Exception as e:
coll = None
def get_total_from_obj(obj):
query_base = 'base_data__one_district__detailData__data_rows__'
sum_dict = obj.prefetch_related('base_data')\
.annotate(A_Area_sum=Coalesce(Sum(query_base+'A_Area'), Value(0)),
B_Area_sum=Coalesce(Sum(query_base+'B_Area'), Value(0)),
C_Area_sum=Coalesce(Sum(query_base+'C_Area'), Value(0)) )\
.aggregate(A_Area_Total=Coalesce(Sum('A_Area_sum'), Value(0)),
B_Area_Total=Coalesce(Sum('B_Area_sum'), Value(0)),
C_Area_Total=Coalesce(Sum('C_Area_sum'),Value(0)) )
return sum_dict
uploaded_files_obj = UploadedExcelFiles.objects.filter(data_range_query,
coated_company_id=coating_company_id).order_by('worked_date')
not_angle = Q(base_data__one_district__detailData__data_rows__product_name__product__in=angle_list)
uploaded_files_list = list(uploaded_files_obj.values('worked_date__day', 'excel_file', 'is_file_locked', 'id'))
for i in uploaded_files_list:
i['excel_file'] = '/media/' + i['excel_file']
total_sum = get_total_from_obj(uploaded_files_obj)
total_Angle_sum = get_total_from_obj(uploaded_files_obj.filter(not_angle))
sum_result = {
'A_Area_Total':round_half_up(total_sum.get('A_Area_Total', 0) - total_Angle_sum.get('A_Area_Total', 0), 2),
'B_Area_Total':round_half_up(total_sum.get('B_Area_Total', 0) - total_Angle_sum.get('B_Area_Total', 0), 2),
'C_Area_Total':round_half_up(total_sum.get('C_Area_Total', 0) - total_Angle_sum.get('C_Area_Total', 0), 2),
}
for k in total_Angle_sum:
total_Angle_sum[k] = round_half_up(total_Angle_sum[k], 2)
sum_new_made = sum_result.get('A_Area_Total', 0) + sum_result.get('C_Area_Total', 0)
sum_recovery_made = sum_result.get('B_Area_Total', 0)
sum_total = sum_new_made + sum_recovery_made
category_list = list(uploaded_files_obj.prefetch_related('base_data')\
.values_list('base_data__one_district__category', flat=True).distinct())
category_list = list(dict.fromkeys(category_list))
category_info = {}
for cat in category_list:
setQ = Q(base_data__one_district__category=cat)
total_sum = get_total_from_obj(uploaded_files_obj.filter(setQ))
_total_Angle_sum = get_total_from_obj(uploaded_files_obj.filter(setQ, not_angle))
for k in _total_Angle_sum:
_total_Angle_sum[k] = round_half_up(_total_Angle_sum[k], 2)
category_info[cat] = {
'result':{
'A_Area_Total':round_half_up(total_sum.get('A_Area_Total',0) - _total_Angle_sum.get('A_Area_Total',0), 2),
'B_Area_Total':round_half_up(total_sum.get('B_Area_Total',0) - _total_Angle_sum.get('B_Area_Total',0), 2),
'C_Area_Total':round_half_up(total_sum.get('C_Area_Total',0) - _total_Angle_sum.get('C_Area_Total',0), 2),
},
'sum_result':{
'new_made':round_half_up(total_sum.get('A_Area_Total',0) +
total_sum.get('C_Area_Total',0) - _total_Angle_sum.get('A_Area_Total',0) ,2),
'recovery_made':round_half_up(total_sum.get('B_Area_Total',0) - _total_Angle_sum.get('A_Area_Total',0), 2),
'total_sum':round_half_up(total_sum.get('A_Area_Total',0)
+ total_sum.get('B_Area_Total',0) + total_sum.get('C_Area_Total',0)
- _total_Angle_sum.get('A_Area_Total',0), 2)
},
'angle':_total_Angle_sum
}
# print(uploaded_files_list)
# print(sum_result)
context = {
'uploaded_files_list':uploaded_files_list,
'uploaded_files_list_length':len(uploaded_files_list),
'sum_result':sum_result,
'sum_new_made':sum_new_made,
'sum_recovery_made':sum_recovery_made,
'sum_total':sum_total,
'total_Angle_sum':total_Angle_sum,
'category_info':category_info,
'date_obj':date_obj,
'coated_company':coated_company,
'coating_company_id':coating_company_id,
}
return render(request, template_name, context)
이게 아직 정리가 안 된 버전인데, 코드를 정리하고 쿼리식도 조금 더 간결하게 한다면 더 나아질 것으로 보인다.(지금말고 더 많이 나중에 한다는 말.)
이 결과는,,, 두둔,,
485개를 12개로 줄였다. 이게 다이어트지
근데 이거도 내가 조금 삽질을 해서 그렇지, 쿼리식을 조금 더 만지면 더 간단하게 할 수 있을듯 하다. 그치만, 여기에는 큰 비밀이 있다.
사실 일자버튼을 클릭하면 데이터를 아래처럼 띄우는데,
이거 하나를 위해서 딱히 모든 데이터를 한 번에 불러올 필요가 없었던 것.
그래서 일자부분을 통해서 데이터를 불러오는 과정은 ajax를 통해 처리했다.
그 방법은 아약스 요청을 통해 렌더링된 페이지 소스를 받아다가 innerHTML로 사이에다가 때려박고 onclick 이벤트도 기존 toggle_show 형식으로 바꿔준 후에 마치 '데이터를 이전에 이미 다 받아온 양' 인 척 하게끔 했다. 사실은 요청버튼을 누르면서 데이터를 다운받는다.
def get_rendered_one_district_info(request):
file_id = request.POST.get('file_id', None)
coating_company_id = request.POST.get('coating_company_id', None)
angle_list = ['ANGLE', 'ANGLE ', 'angle', 'Angle']
result = UploadedExcelFiles.objects.get(id=file_id).base_data.prefetch_related('one_district')\
.annotate(
worked_date = F('finishing_date'),
company = F('one_district__company'),
location = F('one_district__location_name'),
district = F('one_district__district'),
category = F('one_district__category'),
detail_id = F('one_district__detailData_id'),
new_made = Coalesce(Sum('one_district__detailData__data_rows__A_Area'), Value(0))
+ Sum('one_district__detailData__data_rows__C_Area')
- Coalesce(Sum(
Case(
When(
one_district__detailData__data_rows__product_name__product__in=angle_list,
then=F('one_district__detailData__data_rows__A_Area')
)
),
output_field=DecimalField(), default=0),Value(0)),
recovery_made = Coalesce(Sum('one_district__detailData__data_rows__B_Area'), Value(0)),
total_sum = Coalesce(
Sum('one_district__detailData__data_rows__A_Area')
+ Sum('one_district__detailData__data_rows__B_Area')
+ Sum('one_district__detailData__data_rows__C_Area')
- Sum( Case(
When(
one_district__detailData__data_rows__product_name__product__in=angle_list,
then=F('one_district__detailData__data_rows__A_Area')
)
), output_field=DecimalField(), default=0), Value(0)),
angle_sum = Coalesce(Sum(
Case(
When(
one_district__detailData__data_rows__product_name__product__in=angle_list,
then=F('one_district__detailData__data_rows__A_Area')
)
),
output_field=DecimalField(), default=0),Value(0)),
).values('company', 'location', 'district', 'new_made',
'recovery_made', 'category', 'angle_sum', 'detail_id', 'worked_date', 'total_sum')
html_list = []
for ctx in result:
html_list.append(render_to_string('components/excel_file_list_group_item.html', ctx ))
data = {
'result':html_list
}
return JsonResponse(data)
// javascript
function toggle_show(ths){
var data_day = ths.dataset.day;
// var target_elements = $('.day'+data_day);
var target_elements = $('div[name ="day'+ data_day +'"]');
if (target_elements.hasClass("list-hide")){
target_elements.removeClass('list-hide');
return
}
else {
target_elements.addClass('list-hide');
return
}
}
// ...
function uploaded_file_info_request(e){
console.log(e);
console.log(e.dataset.file_id);
var coating_company_id = {{ coating_company_id }};
var data = {
'file_id':e.dataset.file_id,
'coating_company_id':coating_company_id,
};
var path_url = "{% url 'ajax_get_uploaded_info' %}";
$.ajax({
type : 'post',
url : path_url,
data : data,
dataType : 'json',
error: function(xhr, status, error){
alert(error);
},
success : function(json){
console.log('on ajax');
e.parentElement.parentElement.insertAdjacentHTML('afterend', json['result'][0]);
for (var i = 0; i < json['result'].length; i++) {
e.parentElement.parentElement.insertAdjacentHTML('afterend', json['result'][i]);
}
e.onclick = function(){toggle_show(e);};
console.log(e);
console.log(json);
},
});
}
이걸 위해서 지옥같은 쿼리식을 두 번이나 짰어야 했다.
이렇게 Ajax와 When, Sum, Case, prefetch_related, select_related, Q, F 를 이용해서 쿼리식을 최소한으로 줄였다. 그치만 내 스트레스는 줄이지 못했다.(...)
뿌듯하긴하다.
#
'코드 자가리뷰(장고)' 카테고리의 다른 글
장고 쿼리셋 최적화 예제 복기(django queryset optimization) (0) | 2020.09.04 |
---|---|
장고 aws ec2 배포 메모 (0) | 2020.08.30 |
ORM코드 최적화가 필요하다. (0) | 2020.08.23 |
model.py에는 온갖 정성을 쏟아붓자. (0) | 2020.08.20 |
장고 테스트 코드 작성기 (0) | 2020.08.08 |