작성
·
1K
0
지금 context라는 변수를 첫 번째 if문에 한번 두 번째 if문에 한번 총 두 번 선언합니다.
각 if문 안에 있는 동작이 끝나는 순간 함수가 끝나기 때문에 서로 영향을 주지 않습니다.
그런데, 첫 번째 context에 있는 변수들은 html 페이지에 잘 뜨는데 두 번째 context에 있는 변수들이 html로 넘어가지 않습니다.
예를 들어, 두 번째 context에 있는 mape_val이 html에서 출력 되지 않습니다.
데코레이터 코드는 참고용으로 넣어두었습니다. views.py 부터 봐주시면 감사하겠습니다.
<views.py>
@navsDecorator
@viewDecorator
def csv_forecast(req, context):
username = req.user
if req.method == 'POST' and req.FILES.get('csvfile'):
uploaded_file = req.FILES.get('csvfile')
p_data = pd.read_csv(uploaded_file)
p_data.reset_index(drop=True, inplace=True)
columns_list = list(p_data.columns)
columns_list = [column.lower() for column in columns_list]
p_data.columns = columns_list
# 디렉토리 없는 경우 생성해주는 python 내장 모듈
os.makedirs('media/csv', exist_ok=True)
p_data.to_csv(f'media/csv/{username}.csv', index=False)
start_date = p_data.loc[0, 'date']
len_date = int(len(p_data)*0.8)
end_date = p_data.loc[len_date, 'date']
datas = []
for i in range(1, len(columns_list)):
datas.append(columns_list[i])
MODEL_LIST = ['ARIMA', 'EMA5', 'LSTM']
context = {'datas' : datas, 'd' : p_data, 'columns_list' : columns_list, 'MODEL_LIST' : MODEL_LIST,
'start_date' : start_date, 'end_date' : end_date}
# req.POST.get('')과 req.POST['']의 차이는 get을 적었을때는 키값이 존재하지 않는 경우 None값을 반환함
if req.POST.get('sendModel') and req.POST.get('sendPdata') and req.POST.get('sendRdata'):
# js에서 ajax로 보낸 dict의 value값
send_pdata = req.POST.get('sendPdata')
send_rdata = req.POST.get('sendRdata')
send_model = req.POST.get('sendModel')
cleaned_pdata = re.split(r'[\[\],"]', send_pdata)
cleaned_rdata = re.split(r'[\[\],"]', send_rdata)
cleaned_model = re.split(r'[\[\],"]', send_model)
selected_pdata = [i for i in cleaned_pdata if len(i) >= 1]
selected_rdata = [i for i in cleaned_rdata if len(i) >= 1]
selected_model = [i for i in cleaned_model if len(i) >= 1]
csv_data = pd.read_csv(f'media/csv/{username}.csv')
mape_val, y_pred, y_test, test_date = model_main(csv_data, selected_pdata,selected_rdata, selected_model)
print(mape_val)
# print(type(y_pred))
# print(type(y_test))
# print(type(test_date))
fs = FileSystemStorage(location=f'media/csv/')
fs.delete(f'{username}.csv')
context = {'mape_val': mape_val,'y_pred' : y_pred, 'y_test' : y_test, 'test_date' : test_date }
return req, context
<csv_forecast.html>
<body>
{% include "nav.html" %}
<div class="container">
<!-- css 깨지고 검색안되는거 forecast_scripts 문제였음 / 그 외 materialAutoComplete 추가 / views.py name get으로 가져오는 것 수정 -->
<div class="card">
<div class="card-body">
{% if datas %}
<h3>데이터와 모델 선택 후 하단에 저장버튼을 클릭해주세요.</h3>
{% endif %}
<br>
<div class="sub-section" style="overflow: hidden;">
<form method="post" enctype="multipart/form-data">
<!-- form에서 받는 모든 정보를 모두 인코딩하지는 않는다는 뜻으로 이 부분을 설정하지 않으면 form에서 filename 정도만 받아오게 된다.
이 방식은 파일이나 이미지를 서버로 전송할 때 주로 사용한다. -->
{% csrf_token %}
<input type="file" name="csvfile" accept=".csv">
{% if datas %}
<button type="submit" class="btn btn-primary">CSV 파일 업로드 완료</button>
{% else %}
<button type="submit" class="btn btn-secondary">CSV 파일 업로드 </button>
{% endif %}
</form>
<br>
<h3>학습구간 : {{start_date}} ~ {{end_date}}</h3>
</div>
</div>
</div>
<div class="col">
<h3>데이터 선택</h3>
</div>
<div class="card" >
<div style="height: 400px; overflow: overlay">
<div class="card-body">
<!-- <button type="button" class="btn btn-primary" id="select_data">선택 데이터 저장</button> -->
<div class="sub-section" style="overflow: hidden;">
<div class="sub-section01">
<table class="table table-hover table-centered table-nowrap" id="table_data">
<thead class="thead-dark">
<tr>
<th></th>
<th>예측 데이터</th>
<th>근거 데이터</th>
<th>딜레이</th>
</tr>
</thead>
<tbody>
{% for data in datas %}
<tr>
<td>{{data}}</td>
<td><input type="radio" name ="pdata_checkbox"></td>
<td><input type="checkbox" name ="rdata_checkbox"></td>
<td>1</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div id="checked_data"></div>
</div>
</div>
</div>
</div>
<div class="col">
<h3>모델 선택</h3>
</div>
<div class="card" >
<div class="card-body">
<div class="sub-section" style="overflow: hidden;">
<div class="sub-section01">
<table class="table table-hover table-centered table-nowrap" id="table_model">
<thead class="thead-dark">
<tr>
<th>선택</th>
<th>예측 모델</th>
</tr>
</thead>
<tbody>
{% for model in MODEL_LIST %}
<tr>
<td><input type="radio" name ="model_checkbox"></td>
<td>{{model}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if datas %}
<button type="button" class="btn btn-primary" id="select_model">선택 데이터와 모델 저장</button>
{% endif %}
<div id="checked_model"></div>
</div>
</div>
</div>
<div class="col">
<h3>예측 결과</h3>
</div>
<div class="card">
<div class="card-body">
<div class="sub-section" style="overflow: hidden;">
<h3>{{mape_val}}</h3>
</div>
</div>
</div>
</div>
<decorator.py>
def viewDecorator(func):
def wrapper(*args, **kwargs):
req = args[0]
context = {key: val for key, val in req.GET.dict().items()}
kwargs['context'] = context
req, context = func(*args, **kwargs)
return TemplateResponse(req, f'{func.__name__}.html', context=context)
return wrapper
def navsDecorator(function):
@functools.wraps(function)
def wrapper(*args, **kwargs):
template = function(*args, **kwargs)
try:
navs = [
# getNav('main', '퀀트평가'),
# getNav('getGraphByCode', '시즈널리티'),
getNav('csv_forecast', '원자재 예측'),
# getNav('house', '부동산 예측'),
# getNav('similarData', '유사차트'),
]
template.context_data['navs'] = navs
return template.render()
except Exception as e:
print(e)
return template
return wrapper
답변 3
1
안녕하세요.
답은 가까이 있습니다. mape_val 키로 설정한 값이 TemplateResponse 호출 시에 context 사전에 지정되지 않은 것일 겁니다. IDE (VSCode나 PyCharm Professional 등)를 통해 디버깅 기능을 사용하셔서 1 line씩 실행을 시켜보시면서, 생각하신 로직이 실행이 되는 지 context 사전의 값이 생각하신 대로 전달이 되는 지 확인해보시면 금방 원인을 찾으실 수 있으실 것입니다. IDE의 디버깅 기능을 잘 쓰시는 것이 곧 실력입니다.
그리고, csv_forecast 함수에서 context 로 받은 인자를 사용하지 않고 context 이름으로 overwrite를 하던데요. context 사전을 update 하는 형태로 구현이 되어야 하지 않을까요?
그리고, viewDecorator와 navsDecorator의 구현이 장고 기본에서 벗어나서, 과한 매직(Magic)라는 생각이 듭니다. 파이썬/장고는 매직을 최소화하고 가독성을 높이는 것을 중요하게 여기거든요. navsDecorator 코드 대신에 장고 템플릿 기능을 좀 더 활용해보시면 어떠실까 싶구요. viewDecorator보다는 뷰 단에서 직접 템플릿 명을 지정하시는 것은 어떨까요?
차근차근 살펴보시고, 질문 남겨주세요.
화이팅입니다. :-)
0
말씀하신대로 데코레이터 다 삭제하고 기초적인 return render로 코드 간결하게 수정하였습니다.
디버깅을 찍어보니 if문도 잘 걸리고 render 직전에 context를 찍어봐도 제대로 들어가있습니다..
또한, context processor도 사용해보았지만 단순한 string이나 모델 쿼리셋을 넘기는 작업은 가능하나 제가 context에 넣는 변수들은 시간이 오래걸리는 함수를 거쳐서 나오는 return 값이기 때문에 제약이 있습니다 ㅠㅠ
모든 페이지에서 context processor가 동작하게끔 하면 그 오래걸리는 함수가 계속 실행되기 때문에 안되고 views.py에서 걸었던 if문을 그대로 가져와서 'mape_val' 값을 다르게 넣어보았더니 'file'로 바뀌는건 동작하는데 'parameter'로는 바뀌지 않습니다.
분명 두번째 if문이 디버깅에 걸려서 return값이 'parameter'로 바뀌었는데도 말이죠. 웹 사이트에서만 안뜹니다 ..
두번째 if문을 지난 후 render되는 시점에서 문제가 되는 것 같습니다.
0
친철한 답변 감사합니다.
말씀하신 대로 render 직전(페이지가 뜨기 직전)까지 context를 찍어보았는데 정상적으로 잘 출력됩니다.
중간중간 view에서, viewDecorator, navsDecorator 내부에서도 모두 잘 출력됩니다.
또한, 데코레이터를 주석처리하고 view에서 직접 TemplateResponse 사용한 후 return 직전에 context를 디버깅으로 찍어봐도 잘 출력이 됩니다. TemplateResponse 내부에서 문제가 생기는 걸까요?
viewDecorator에서 굳이 TemplateResponse를 반환하고 이를 navsDecorator에서 받아서 context_data 값을 변경하고 render하는 이유를 잘 모르겠습니다. 단순한 코드인데 너무 복잡하게 쓰신 듯 합니다. // 일단 먼저 말씀드린 것처럼 viewDecorator와 navsDecorator를 통해 꼬아놓으신 코드를 풀어서 간결하게 구현해보세요. 꼬아놓은 상황 자체를 해결하지 않고 다른 부분을 고치다보면 문제해결이 더 산으로 가게 됩니다.
참고로 템플릿 내에서는 장고 디폴트로 context processors로서 request 객체를 쓰실 수 있습니다. 그러니 템플릿 내에서 별도의 context data를 넘기지 않아도 {{ request.GET.key1 }} 와 같은 식으로 참조하실 수 있습니다. // navs는 custom template tags를 만드셔서 로딩하시면 더욱 좋은 접근일 듯 싶네요.
원인은 하나 밖에 없습니다. 템플릿 렌더링 시점에 context data에 원하시는 값이 없으면, 그 값을 템플릿 내에서 참조할 수 없을 것입니다. 그런데 꼬아놓은 코드로 인해 그러한 로직이 잘 파악이 안 되는 것일테구요.
차근차근 파악해보시면서 코드를 간결하고 가독성높게 쓰실려고 노력해보세요.
화이팅입니다. :-)
render 함수 수행 시에 지정된 context 를 통해 렌더링을 수행합니다. 값이 지정되었는 데 이 값을 템플릿 내에서 참조하지 않는 경우는 없습니다.
context에 값이 지정되지 않으셨거나
엉뚱한 템플릿 파일에서 값을 참조하셨거나, 템플릿에서 엉뚱한 이름으로 값을 참조하셨거나
혹은 응답이 있었는 데, 이 응답을 체크하는 방법이 잘못 되었거나. 체크를 잘못하셨거나. 입니다. Ajax 로 응답을 받으셨다면 브라우저 Network탭을 통해 확인해보셔도 좋겠구요.
제가 더 이상 드릴 수 있는 조언은 없는 듯 합니다. 차근차근 파악해보세요.
화이팅입니다. :-)