25. 2. ORM뒤에
숨겨진 블랙홀
Django 개발자가 된 lv1 야도란
ORM을 만나서 진화-@@@!
쿼리를 낚다가..
DevDjango Korea
26. SQL 그게 뭐야? :)
SELECT
FROM
JOIN
GROUB BY
WHEN
ON
WHERE
INSERT
2. ORM뒤에
숨겨진 블랙홀
DevDjango Korea
27. 얍!
얍!
얍!
OH! ORM 찬양! OH!
포스트 다 가져오기
포스트 1개 가져오기
내가 쓴 포스트 가져오기
Post.objects.all()
Post.objects.get(id=1)
Post.objects.filter(username=“hyang”)
2. ORM뒤에
숨겨진 블랙홀
DevDjango Korea
28. 실제 상황 1
USER:
2. ORM뒤에
숨겨진 블랙홀
저희 머신러닝 모델의 여러 버전들마다 실행된 것들에서
8월 1일에서 8월 20일까지 실행된 것 들 중
error없이 실행된 active한 것들만 모아서
응답시간의 평균 값, 최대값
그리고 모델에 설정한 스레숄드 값보다 빠르게 응답한 개수를
보여주는 화려한 대시보드 만들어주세요 ^^
“
“
DevDjango Korea
30. SQL 강제 공부 돌입
SELECT
FROM
JOIN
GROUB BY
WHEN
ON
WHERE
얘네 가져올꺼야 이럴 때만
얘네들만 묶어서
저기서부터 가져와서
교집합 차집합 합집합..
이런 것들만
2. ORM뒤에
숨겨진 블랙홀
잘못했어요..
살려주세요..
DevDjango Korea
31. 드디어 쿼리문 완성!!
SELECT
AVG(run.response_time) as "avg_response_time"
, Max(run.response_time) as "max_response_time"
, SUM(1) as num_runs
, SUM(case when run.response_time > ml_model.slow_run_threshold_in_milliseconds then 1
else 0 end) as num_fast_runs
FROM run
JOIN version on version.id = run.version_id
JOIN ml_model ml_model on run. ml_model_id = ml_model.id
WHERE run.requested_at >= '2018-08-01'::timestamp with time zone AND run.requested_at <
'2018-08-20'::timestamp with time zone
AND run.is_active = True
AND version.id in (versions_ids)
2. ORM뒤에
숨겨진 블랙홀
짜잔!
DevDjango Korea
32. 쿼리도 쓰고 대단해!!
2. ORM뒤에
숨겨진 블랙홀
근데..
어떻게 썼더라..
DevDjango Korea
34. ORM 응급실
ORM에대한 희망을 잃지 말아요~
도큐먼트에는 다 있으니까~
2. ORM뒤에
숨겨진 블랙홀
DevDjango Korea
35. 필요한 데이터 뽑기!
2. ORM뒤에
숨겨진 블랙홀
저희 머신러닝 모델의 여러 버전마다 실행된 것들에서
8월 1일에서 8월 20일까지 실행된 것 들 중
error없이 실행된 active한 것들만 모아서
응답시간의 평균 값, 최대값
그리고 저희 모델에 설정한 스레숄드 값보다 빠르게 응답한 개수를
보여주는 화려한 대쉬보드 만들어주세요 ^^
저 조건들 다 filter에 넣고 한 번에 가져올 수 있으면 좋겠다..
“
“
DevDjango Korea
36. 가져올 수 있음!!
2. ORM뒤에
숨겨진 블랙홀
run_qs = Run.objects.filter(
version__id__in=version_ids,
ml_model__id__in=version_ids,
is_active=True,
requested_at__range=(start_date, end_date)
)
Filter에 ForeignKey도 조건으로 막 넣고!
list에 포함되는지는 in! 범위는 range!
DevDjango Korea
39. 평균? 최대값?
2. ORM뒤에
숨겨진 블랙홀
저희 머신러닝 모델의 여러 버전마다 실행된 것들에서
8월 1일에서 8월 20일까지 실행된 것 들 중
error없이 실행된 active한 것들만 모아서
응답시간의 평균 값, 최대값
그리고 저희 모델에 설정한 스레숄드 값보다 빠르게 응답한 개수를
보여주는 화려한 대쉬보드 만들어주세요 ^^
“
“
DevDjango Korea
41. Aggregation!!!
Aggregate를 사용해서 한 컬럼에 전체에 대해 계산할 수 있어요!
2. ORM뒤에
숨겨진 블랙홀
run_qs.aggregate(Avg('response_time'), Max('response_time'))
DevDjango Korea
42. Level up!!!
run.response_time > ml_model.threshold인
run의 총개수???
2. ORM뒤에
숨겨진 블랙홀
저희 머신러닝 모델의 여러 버전마다 실행된 것들에서
8월 1일에서 8월 20일까지 실행된 것 들 중
error없이 실행된 active한 것들만 모아서
응답시간의 평균 값, 최대값
그리고 저희 모델에 설정한 스레숄드 값보다 빠르게 응답한 개수를
보여주는 화려한 대쉬보드 만들어주세요 ^^
“
“
DevDjango Korea
43. 조건문은??
Case와 When을 쓰기!
2. ORM뒤에
숨겨진 블랙홀
fast_runs=Count(
Case(
When(response_time__gt=F('ml_model_threshold'), then=1),
output_field=IntegerField(),
)
)
DevDjango Korea
44. Annotation!!!
특정 기준으로 값을 묶어 새로운 컬럼만들때는 annotation!
2. ORM뒤에
숨겨진 블랙홀
run_qs.annotate(
fast_runs=Count(
Case(
When(response_time__gt=F('ml_model_threshold'), then=1),
output_field=IntegerField(),
)
)
).aggregate(Sum('fast_runs'))
DevDjango Korea
45. Aggregation!!!
그렇게 만든 컬럼을 다시 합쳐서 계산하면 끝!
2. ORM뒤에
숨겨진 블랙홀
run_qs.annotate(
fast_runs=Count(
Case(
When(response_time__gt=F('ml_model_threshold'), then=1),
output_field=IntegerField(),
)
)
).aggregate(Sum('fast_runs'))
DevDjango Korea
51. ORM 쉽다며...
믿었던 ORM으로 만든 지뢰밭..
어디서 터지고 왜 터지는지 이해할 수가 없어요..
2. ORM뒤에
숨겨진 블랙홀
DevDjango Korea
52. ORM의 비밀..?!
세상에나, Run.objects.all()에서 터진게 아니었다?
여기서 터졌다고 해요
2. ORM뒤에
숨겨진 블랙홀
runs = Run.objects.all()
for run in runs:
print(run.name)
WHY?!!!!
DevDjango Korea
53. 왜???
사실 ORM은 매우 매우 게을러서(?)
ORM아 그렇게
놀기만 하다가
서버가 터지면
어쩌려고 그러니
2. ORM뒤에
숨겨진 블랙홀
공부나해
ORM아 그렇게
놀기만 하다가
서버가 터지면
어쩌려고 그러니
DevDjango Korea
54. 왜???
진짜진짜 필요할 때까지는 DB에서 레코드를 가져오지 않는다고 해요.
…
무념무상
2. ORM뒤에
숨겨진 블랙홀
runs = Run.objects.all()
for run in runs:
print(run.name)
DevDjango Korea
55. 왜???
가져오려면 이렇게 쿼리셋을 순회해야..
더 이상 미룰수 없음..
2. ORM뒤에
숨겨진 블랙홀
가져
올게runs = Run.objects.all()
for run in runs:
print(run.name)
DevDjango Korea
56. 심지어 저장해둠
나중에 또 쓸까봐 한 번 가져오면 캐시로 저장해둔다고해요
runs = Run.objects.all()
for run in runs:
print(run.name)
for run in runs:
print(run.name)
2. ORM뒤에
숨겨진 블랙홀
DevDjango Korea
이미
있음
57. 그럼 실제로 쿼리를 어떻게 날리는걸까?
Django-debug-toolbar를 실행시켜봅니다.
오오 어디서 쿼리를 어떻게 날렸는지 다 알려줘요!!
2. ORM뒤에
숨겨진 블랙홀
DevDjango Korea
58. 그런데..!
중복되는 쿼리가 굉장이 많다...!
코딩할 때 뭐든 중복은 안 좋은 거랬어..!
2. ORM뒤에
숨겨진 블랙홀
DevDjango Korea
59. 검색검색
쿼리 최적화하려면
데이터 set을
“필요한 만큼만”
“웬만하면 한 번에!”
쏙쏙 가져와야한대요
인터넷에서 모은 꿀 팁들
2. ORM뒤에
숨겨진 블랙홀
DevDjango Korea
60. 먼저, 한 번에 가져오기!
ForeignKey는 select_related!
ManyToManyField는 prefatch_related!
2. ORM뒤에
숨겨진 블랙홀
DevDjango Korea
61. 예를들면,
class Post(models.Model):
author = models.ForeignKey(Author)
tag = models.ManyToManyField(Tag)
2. ORM뒤에
숨겨진 블랙홀
author을 ForeinKey
tag를 ManyToMany로가진
Post 모델!!
DevDjango Korea
63. ForeignKey는 select_related!
post = Post.objects.get(id=1)
author = post.author
2. ORM뒤에
숨겨진 블랙홀
author가져올 때 한 번
이렇게 가져오면 DB에 두번이나 갔다와야함
정색 DevDjango Korea
64. 한 번에 가져오기!
select_related로 한 번에 가져오기!
방긋
post = Post.objects.select_related('author').get(id=1)
author = post.author
author값도 같이 가져와!
2. ORM뒤에
숨겨진 블랙홀
굿굿
DevDjango Korea
65. ManyToManyField는 prefatch_related!
posts = Post.objects.all()
for post in posts:
for tag in post.tag_set.all():
print(tag)
2. ORM뒤에
숨겨진 블랙홀
post 하나 돌 때마다
DevDjango Korea
66. ManyToManyField는 prefatch_related!
posts = Post.objects.all()
for post in posts:
for tag in post.tag_set.all():
print(tag)
2. ORM뒤에
숨겨진 블랙홀
tag_set 가져오기..?
posts 백개면 백번..천개면 천번..
…
DevDjango Korea
67. posts = Post.objects.all().prfetch_related(‘tag_set')
for post in posts:
for tag in post.tag_set.all():
print(tag)
두 번으로 줄이기
감격
post다 가져온다음 tag_set도 다 가져와!
post.objects.all()
self.tag_set.all)()
2. ORM뒤에
숨겨진 블랙홀
최고!
DevDjango Korea
ManyToManyField는 prefatch_related!
68. 필요한 것만 가져오기!
아무 생각 없이 다가져오던 시절..
메모리따위 고려하지 않겠어!!
posts = Post.objects.all()
for post in posts:
print(post.name)
메모리
ㅠ ㅠ
2. ORM뒤에
숨겨진 블랙홀
DevDjango Korea
69. 필요한 것만 가져오기!
어떻게든 values, values_list로 줄여요…
ORM 무서워..
posts = Post.objects.values('name')
for post in posts:
print(post.name)
2. ORM뒤에
숨겨진 블랙홀
메모리
^ ^
DevDjango Korea
70. 있는지 없는지 확인하는데
posts 다 가져오면 낭비니까!
posts = Post.objects.all()
If posts:
print(posts)
2. ORM뒤에
숨겨진 블랙홀
DevDjango Korea
필요한 것만 가져오기!
71. 전부 vs 하나
가져오기
posts = Post.objects.all()
If posts.exists():
print(posts)
하나만 있는지 확인!
2. ORM뒤에
숨겨진 블랙홀
DevDjango Korea
필요한 것만 가져오기!
80. 3. 트랜젝션 적용하다
Celery에게 뒷통수 맞다
발견!!!
저 create() 함수에서 부모객체, 자식객체 둘 다 생성하군!
version
자식객체 생성!
ml_model
부모객체 생성!
def create(self, request, *args, **kwargs):
ml_model = Mlmodel.objects.create(
title=request.data.get('title', None)
)
tasks.build(ml_model.id)
version = Version.objects.create(
parent=ml_model
)
DevDjango Korea
81. 3. 트랜젝션 적용하다
Celery에게 뒷통수 맞다
상상중...
자식 생성 도중 실패하면?
그럼 이미 만들어진 부모 객체는??
DevDjango Korea
version
자식객체 생성 실패!
ml_model
부모객체 생성!
def create(self, request, *args, **kwargs):
ml_model = Mlmodel.objects.create(
title=request.data.get('title', None)
)
tasks.build(ml_model.id)
version = Version.objects.create(
parent=ml_model
)
113. 3. 트랜젝션 적용하다
Celery에게 뒷통수 맞다
역시 Django!!
Django에서 이런 때를 대비해!!
on_commit이라는 훅을 제공한대요
on_commit
transaction끝난 뒤
호출됩니다!!
DevDjango Korea
114. 3. 트랜젝션 적용하다
Celery에게 뒷통수 맞다
Celery 기다려!
on_commit 훅을 사용해서 커밋이 다 끝난뒤
셀러리가 task를 하도록 설정!!
transaction.on_commit(lambda: celery_task.delay(‘version_id’))
DevDjango Korea
115. 3. 트랜젝션 적용하다
Celery에게 뒷통수 맞다
그리하여 celery가 commit을 기다린 후
Django Celery
DB
commit!
기다려!
DevDjango Korea
ml_model =
Mlmodel.objects.create()
version =
Version.objects.create()
ml_model row
version row
116. 3. 트랜젝션 적용하다
Celery에게 뒷통수 맞다
모델을 가져오도록 적용할 수 있었다고..
Django Celery
DB
ml_model row
ml_model!!!
version row
get!
DevDjango Korea
ml_model =
Mlmodel.objects.create()
version =
Version.objects.create()
117. 3. 트랜젝션 적용하다
Celery에게 뒷통수 맞다
삽질했지만!
혼자 문제만들고 삽질하고...그래도 해결하고보니
완전 뿌듯해!!!!
DevDjango Korea
148. 상상
유저가 브라우저에서 우리 서비스에 로그인하고 인터넷을 떠돌아다니다
User1 반가워요!
4. 헌집줄게 새집다오 DevDjango Korea
sessionid:
r37046e1944d532e3dc
2adf0d5c483fc
(주의! session id를 사용하여 인증할 때의 예를 든 것이에요!)
쿠키
149. 4. 헌집줄게 새집다오 DevDjango Korea
어머나! 피싱사이트에 접속!
풰이크북.com
Save
상상
150. 4. 헌집줄게 새집다오 DevDjango Korea
풰이크북.com
홀라당 덫에 걸려 CSRF공격 코드가 삽입된 버튼 click!!!
Save
나는. 유노윤호다.!
상상
151. 4. 헌집줄게 새집다오 DevDjango Korea
풰이크북.com
Save
나는. 유노윤호다.!
<form action=“http://knowru.com/
user/reset_password/“
method=“POST”>
<input type=“hidden”
name=“password” value=“1234">
</form>
폼에는 우리 서비스에 비밀번호를 리셋하는 코드가..!
상상
152. 4. 헌집줄게 새집다오 DevDjango Korea
브라우저에 저장된 쿠키와 함께 우리 서버로 요청을 보냈어요!
POST
http://knowru.com/user/
reset_password/
{password: ‘1234’}
sessionid:
r37046e1944d532e3dc2adf0d5c483fc
ㅋㅋㅋ
상상
153. 4. 헌집줄게 새집다오 DevDjango Korea
발급해준 sessionid가 일치하니 비밀번호 리셋 성공!
User sessionid 인증성공!
200 OK!
?!
상상
164. Django와 CSRF
User
Django
Server
4. 헌집줄게 새집다오 DevDjango Korea
서버가 두 값을 비교검증합니다!
csrftoken:
r37046e1944d532e3dc2adf0d5c
483fc
csrftmiddlewaretoken:
mTGKXN4BWSBd6wavFtKSzBr…
169. 그럼 Ajax는?
뭐지...이 엄청난 코드는....
DevDjango Korea
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length +
1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
갖다쓰렴 ^^
4. 헌집줄게 새집다오
170. 그럼 Ajax는?
유저가 ajax로 전송할 Form 제출 버튼을 클릭하면
DevDjango Korea
Submit
Ajax로 데이터 전송!
4. 헌집줄게 새집다오