Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 29 | 30 | 31 |
Tags
- 코딩부트캠프후기
- not a git repository
- 파이썬 sep
- 개발자사이드프로젝트
- 항해플러스
- 파이썬 클래스
- 파이썬 |
- 10430번
- 99클럽 #99일지 #코딩테스트 #개발자스터디 #항해 #til
- Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding
- 개발자스터디
- print("""
- 주니어개발자역량강화
- 항해
- 99일지
- 99클럽
- Til
- 주니어개발자멘토링
- cp949
- 파이썬 int()
- print sep
- 백준
- 항해99
- 파이썬
- EnvCommandError
- 파이썬 map 함수
- fatal:not a git repository
- vscode cp949
- MomentumParameters
- 코딩테스트
Archives
- Today
- Total
선발대
[스파르타] Django 심화반 3주차 (완강) 본문
수업목표
- pagination, prefetch를 이해한다.
- cascade를 이해한다.
- unit test와 end to end 테스트의 차이를 말할 수 있다.
01. 좋아요 개수를 세기
좋아요 개수를 세기 & manager
## 좋아요 개수 세기
def test_like_count_should_increase(self) -> None:
# Given
user = User.objects.create(name="test")
article = Article.objects.create(title="test_title")
# When
do_like(user.id, article.id)
# Then
article = Article.objects.get(id=article.id)
self.assertEqual(1, article.like_set.count())
- article에 달려있는 like는 like_set을 통해 접근할 수 있음.
- 해당 게시글에 달려있는 모든 좋아요의 개수를 알고 싶다면 count() 함수 사용
- 확인하면 좋아요 개수가 0에서 1로 잘 증가한 것을 확인 가능.
- like_set은 manager 객체임.
- like는 foreignkey로 article을 가리켰기 때문에 article에 like_set이라는 이름의 RelatedManager 생김.
- manager: model 객체와 데이터베이스를 이어주는 통로 역할
- objects 통해 쿼리하면 모델 객체 얻을 수 있음. 이것이 장고다.
- 예시: Like.objects.all()
- like_set도 objects와 같은 manager 이므로 objects. 에서 할 수 있던 필터링, 쿼리 등을 동일하게.
02. 좋아요 취소
좋아요 취소
- 자신의 좋아요 삭제할 수 있게 하기
- 좋아요가 없을 때는 에러가 발생함
## like_service.py
from tabom.models.like import like
def do_like(user_id: int, article_id: int) -> Like:
return Like.objects.create(user_id=user_id, article_id=article_id)
def undo_like(user_id: int, article_id: int) -> None:
pass:
## test_like_service.py
# 자신의 좋아요 삭제하기
def test_a_user_can_undo_like(self) -> None:
# Given
user = User.objects.create(name="test")
article = Article.objects.create(title="test_title")
like = do_like(user_id=user.id, article_id=article.id)
# When
undo_like(user.id, article.id)
# Then
with self.assertRaises(Like.DoesNotExist):
Like.objects.get(id=like.id)
# 좋아요 없을 때는 에러가 발생함
def test_it_should_raise_exception_when_undo_like_which_does_not_exist(self) -> None:
# Given
user = User.objects.create(name="test")
article = Article.objects.create(title="test_title")
# Expect
with self.assertRaises(Like.DoesNotExist):
undo_like(user.id, article.id)
- DoesNotExist: queryset에 get()을 했는데 단 한건도 조회되지 않을 때 발생함.
- 공식문서: https://docs.djangoproject.com/ko/4.0/ref/models/class/#doesnotexist
## like_service.py
# undo_like 구현하기
from tabom.models.like import Like
def do_like(user_id: int, article_id: int) -> Like:
return Like.objects.create(user_id=user_id, article_id=article_id)
def undo_like(user_id: int, article_id: int) -> None:
like = Like.objects.filter(user_id=user_id, article_id=article_id) # 1개만 있음
like.delete()
queryset에 직접 delete()를 하는 방법
- like가 존재하지 않더라도 error 가 발생하지 않도록 하는 방법도 있음.
## like_service.py
from tabom.models.like import like
def do_like(user_id: int, article_id: int) -> Like:
return Like.objects.create(user_id=user_id, article_id=article_id)
def undo_like(user_id: int, article_id: int) -> None:
Like.objects.filter(user_id=user_id, article_id=article_id).delete()
- SELECT 쿼리 없이 바로 DELETE를 하기 때문에 objects.delete()보다 쿼리 한번 아낄 수 있음.
- 위와 같은 상황에서 테스트 돌리면,
- test_a_user_can_undo_like()는 성공하지만, DoesNotExist는 발생하지 않아서,
- test_it_should_raise_exception_when_undo_like_an_does_not_exist()는 실패함.
- 따라서 실패하는 테스트는 제거하고, queryset의 delete()를 사용할 것임. 쿼리 절약.
03. 게시글 단 건 조회
게시글 단 건 조회
- ariticle 하나를 조회하는 기능 만들어보기
## services/article_service.py
from tabom.models import Article
def get_an_article(article_id: int) -> Article:
#pass
return Article.objects.get(id=article_id)
## tests/test_article_service.py
from django.test import TestCase
from tabom.models.article import Article
from tabom.services.article_service import get_an_article
# 한번 테스트가 끝나면 롤백이 이루어지기 때문에 다른 테스트에 영향 주지 않음.
# 테스트의 isolation
class TestArticleService(TestCase):
def test_you_can_get_an_article_by_id(self) -> None:
# Given
title = "test_title"
article = Article.objects.create(title=title)
# When
result_article = get_an_article(article.id)
# Then
self.assertEqual(article.id, result_article.id)
self.assertEqual(title, result_article.title)
def test_it_should_rasie_exception_when_article_does_not_exist(self) -> None:
# Given
invalid_article_id = 9988
# Exception
with self.assertRaises(Article.DoesNotExist):
get_an_article(invalid_article_id)
04. 게시글 리스트 조회
페이지네이션
- pagination: 전체 리스트를 한 번에 가져오지 않고 쪼개서 가져오는 것
- sql에서 pagination 할 때 알아야 할 2가지:
- 1. offset: 몇 번째 요소부터 가져올 것인지. (기본적으로 id 오름차순으로 정렬됨)
- 2. limit: 몇 개씩 가져올 것인지
- 만약 offset=0, limit=10, 1번째 요소부터 10개 가져옴.
- 페이지의 개념은 offset을 구할 때 사용함. (아래 참고)
- offset: limit * (현재 페이지 - 1)
- article 생성해보기:
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
INSERT INTO tabom_article(title, updated_at, created_at) VALUES ('article', NOW(), NOW());
게시글 리스트 조회
- 요구사항: 게시글 리스트(최신 글이 맨 위로 오도록)을 보여준다.
- 이때 좋아요 받은 개수도 같이 보여준다.
## services/article_service.py
from django.db.models import QuerySet
from tabom.models import Article
def get_an_article(article_id: int) -> Article:
return Article.objects.get(id=article_id)
def get_article_list(offset: int, limit: int) -> QuerySet[Article]:
# pass
## tests/test_article_service.py
# 최신 글 리스트 오름차순으로 보여주기. (+ 좋아요 개수도)
def test_get_article_list_should_prefetch_likes(self) -> None:
# Given
user = User.objects.create(name="user1")
articles = [Article.objects.create(title=f"{i}") for i in range(1, 21)]
Like.objects.create(user_id=user.id, article_id=article[-1].id)
# When
result_articles = get_article_list(0, 10)
# Then
self.assertEqual(len(result_articles), 10)
self.assertEqual(1, result_articles[0].like_set.count())
self.assertEqual(
[a.id for a in reversed(article[10:21])],
[a.id for a in result_articles]
)
- article이 id의 역순 정렬로 나오도록 get_article_list 만들기
- django orm에서는 list slicing으로 pagination을 할 수 있음.
- 공식문서: https://docs.djangoproject.com/en/4.0/topics/db/queries/#limiting-querysets
def get_article_list(offset: int, limit: int) -> QuerySet[Article]:
return Article.objects.order_by("-id")[offset: offset + limit]
- 테스트 실행 후에 성공하는 것도 확인
페이지네이터 클래스 사용
- paginator를 사용하는 방법도 있음.
- 공식문서: https://docs.djangoproject.com/en/4.0/topics/pagination/
## services/article_service.py
def get_article_page(page: int, limit: int) -> Page[Article]:
return Paginator(Article.objects.order_by("-id"), limit).page(page)
## tests/test_article_service.py
def test_get_article_page(self) -> None:
# Given
user = User.objects.create(name="test_user")
articles = [Article.objects.create(title=f"{i}") for i in range(1, 21)]
do_like(user.id, articles[-1].id)
# When
result_articles = get_article_page(1, 10)
# Then
self.assertEqual(len(result_articles), 10)
self.assertEqual(1, result_articles[0].like_set.count())
self.assertEqual(
[a.id for a in reversed(articles[10:21])],
[a.id for a in result_articles]
)
- 강의에서는 페이지네이터가 아닌 slicing을 사용할 것임.
05. 실제 일어나는 쿼리를 조회하기
실제 일어나는 쿼리 조회하기
- 앞서 만든 테스트에서 모든 article의 좋아요 개수 구하기
## 좋아요 개수의 리스트를 만들 수 있음
# When
result_articles = get_article_list(0, 10)
result_counts = [a.like_set.count() for a in articles]
# Then
...
self.assertEqual(1, result_counts[0])
- 코드를 실행할 때 실제 데이터베이스에서 어떤 쿼리가 일어나는지,
- CaptureQueriesContext를 사용해서 알아보기.
## tests/test_article_service.py
def test_get_article_list_should_prefetch_like(self) -> None:
# Given
user = User.objects.create(name="test_user")
articles = [Article.objects.create(title=f"{i}") for i in range(1, 21)]
do_like(user.id, articles[-1].id)
with CaptureQueriesContext(connection) as ctx:
# When
result_articles = get_article_list(0, 10)
result_counts = [a.like_set.count() for a in result_articles]
# Then
self.assertEqual(len(result_articles), 10)
self.assertEqual(1, result_counts[0])
self.assertEqual(
[a.id for a in reversed(articles[10:21])],
[a.id for a in result_articles],
)
- 공식문서: https://docs.djangoproject.com/en/2.2/_modules/django/test/utils/
- 중단점 찍고 디버깅함.
- ctx.captured_quries: 여태까지 실행한 sql이 그대로 들어감. 앞으로도 계속 쓰일 예정.
- 중단점 찍으면 그 바로 앞까지 진행상황(SQL문)을 evalution에서 확인할 수 있음.
- 내부를 보면 각 article별로 좋아요 개수를 세기 위해 쿼리가 실행된 것을 확인할 수 있음.
- 좋아요 개수 모두 구하기 위해 총 10번이나 쿼리를 한 것. 피해라. 1번의 쿼리로 변경해보자.
- 미리 가져오면 쿼리의 개수를 줄일 수 있음.
- article은 가져왔지만 like는 실제로 count()를 호출하기 전까지 데이터베이스에서 가져오지 않았음.
- lazy 하다: evaluate 되기 전까지 sql을 실행하지 않는 것
- eager: lazy의 반대. django에서는 eager loading을 위해 prefetch를 사용함.
06. Prefetch Related
prefetch 사용, assertNumQueries()로 쿼리 횟수 검증하기
- 백엔드의 주요 병목은 데이터베이스 쿼리임.
- 이번에는 prefetch 사용하고, assertNumQueries()로 실행되는 쿼리 횟수를 검증해보기.
## tests/test_article_service.py
def test_get_article_list_should_prefetch_like(self) -> None:
# Given
user = User.objects.create(name="test_user")
articles = [Article.objects.create(title=f"{i}") for i in range(1, 21)]
do_like(user.id, articles[-1].id)
# When
with self.assertNumQueries(2):
result_articles = get_article_list(0, 10)
result_counts = [a.like_set.count() for a in result_articles]
# Then
self.assertEqual(len(result_articles), 10)
self.assertEqual(1, result_counts[0])
self.assertEqual(
[a.id for a in reversed(articles[10:21])],
[a.id for a in result_articles],
)
- 지금은 like_set에서 쿼리가 발생하기 때문에 테스트가 실패함.
- prefetch_related를 사용해보기.
# service/article_service.py
def get_article_list(offset: int, limit: int) -> QuerySet[Article]:
return Article.objects.order_by("-id).prefetch_related("like_set")[offset: offset + limit]
- prefetch는 개념 이해할 때만 어렵게 느껴지지 사용법은 쉽다.
- ./test.sh 를 다시 실행해보면 테스트 끝!
Select Related와 prefetch related
prefetch_related
- many 관계에서 사용하자. (many관계, OneToOne 관계 모두 사용가능)
- WHERE IN()절을 사용해서 쿼리 한 번 더 함.
- 전부 가져오고, 각각의 article에 맞는 좋아요 꽂는 일을 장고가 해준다.
- article 하나에 좋아요 여러 개.
select_related
- OneToOne 혹은 foreign key 관계에서 사용하자. (many에서 쓰면 에러!)
- Join을 사용하기 때문에 추가 쿼리가 일어나지 않음. (내부 구조상의 문제)
Article.objects.prefetch_related("like_set").all()
Article.objects.select_related("like_set").all() # many에서 쓰면 에러!
Like.objects.select_related("user").all() # 쿼리 1번 발생
Like.objects.prefetch_related("user").all() # 에러는 안 나지만 쿼리가 2번 발생
def test_temp(self) -> None:
# Given
user = User.objects.create(name="user1")
article1 = Article.objects.create(title="article1")
like = Like.objects.create(user_id=user.id, article_id=article.id)
Article.objects.create(title="article2")
# Except
with self.assertNumQueries(1):
result_like = Like.objects.select_related("user").get(id=like.id)
self.assertEqual(like.id, result_like.user.id)
# Expect
with self.assetNumQueries(2):
result_like = Like.objects.select_related("user")get(id=like.id)
self.assertEqual(like.id, result_like.user.id)
- select_related: join을 사용해서 가져오기 때문에, 쿼리 개수가 증가하지 않음.
- prefetch_related: prefetch 대상을 가져오기 위해 추가적으로 쿼리를 날림.
join을 처음 접하시는 분들께
- 생활코딩 join: https://www.opentutorials.org/course/3884
- join을 쉽게 설명하는 그림: https://velog.io/@public_danuel/sql-join-is-not-venn-diagram
- join 벤다이어그램: https://sql-joins.leopard.in.ua/
- sql in 100 seconds: https://www.youtube.com/watch?v=zsjvFFKOm3c
07. 이미 좋아요 했는지 여부를 표시하기
이미 좋아요 했는지 여부를 표시하기
- 요구사항: 이미 좋아요 했는지 여부 표시하기
- 게시글에 달린 좋아요는 like_set으로 구할 수 있었음
- 이 중에서 특정 user_id를 가진 like만 가져올 수 있으면 여부 표시할 수 있음.
- 접속한 사용자의 id가 있다면 그건 좋아요를 한 것임.
- 내가 한 좋아요도 한 번에 다 가져올 수 있음.
- 공식문서: https://docs.djangoproject.com/en/4.0/ref/models/querysets/#django.db.models.Prefetch
- Prefetch object 사용해보기: prefetch 할 때 필터링 할 수 있음.
- 여기에 keyword argument to_attr 전달하면 조건에 맞는 relation을 해당 필드에 set 해준다.
- 특정 조건에 맞는(여기에서는 user_id) 것의 특성(my_likes)을 가져오도록 함.
## tests/test_article_service.py
def test_get_article_list_should_contain_my_like_when_like_exists(self) -> None:
# Given
# 유저가 있음, 실제 좋아요 받은 경우. my_likes는 1임.
user = User.objects.create(name="test_user")
article1 = Article.objects.create(title="article1")
like = do_like(user.id, article1_id)
# 유저가 없음 - my_likes는 null임.
Article.objects.create(title="article2")
# When
articles = get_article_list(user.id, 0, 10)
# Then
self.assertEqual(like.id, articles[1].my_likes[0].id)
self.assertEqual(0, len(articles[0].my_likes))
## service/article_service.py
def get_article_list(user_id: int, offset: int, limit: int) -> QuerySet[Article]:
return(
Article.objects.order_by("-id")
.prefetch_related("like_set")
.prefetch_related(Prefetch("like_set", queryset=Like.objects.filter(user_id=user_id), to_attr="my_likes"))[
offset: offset + limit
]
## service/article.py
from typing import Any, List
from django.db import models
from tabom.models.base_model import BaseModel
class Article(BaseModel):
title = models.CharField(max_length=255)
my_likes: List[Any] # Prefetch에서 사용함
- 쿼리 횟수 검증하던 테스트의 쿼리 횟수를 2~3으로 늘려준다.
- with self.assertNumQueries(3): # 원래는 2였음.
- 쿼리횟수를 굳이 늘린 이유: 나중에 prefetch_related("like_set")을 없앨 것이기 때문.
- articles 안에 살펴보기: evaluate에서 [a for a in articles]
08. 단건 조회에도 prefetch 사용하기
단건 조회에서도 Prefetch 사용하기
- 단건조회
# services/
from django.db.models import Prefetch, QuerySet
from tabom.models import Article, Like
def get_an_article(user_id: int, article_id: int) -> Article:
return Article.objects.prefetch_related(
Prefetch("like_set", queryset=Like.objects.filter(user_id=user_id), to_attr="my_likes")
).get(id=article_id)
def get_article_list(user_id: int, offset: int, limit: int) -> QuerySet[Article]:
return (
Article.objects.order_by("-id")
.prefetch_related("like_set")
.prefetch_related(Prefetch("like_set", queryset=Like.objects.filter(user_id=user_id), to_attr="my_likes"))[
offset: offset + limit
]
)
test case 추가
## test case 추가
# 추가 테스트 케이스
from django.test import TestCase
from tabom.models import Like, User
from tabom.models.article import Article
from tabom.services.article_service import get_an_article, get_article_list
from tabom.services.like_service import do_like
class TestArticleService(TestCase):
def test_you_can_get_an_article_by_id(self) -> None:
# Given
title = "test_title"
article = Article.objects.create(title=title)
# When
# user.id는 상관없어서 그냥 0 넣었음
result_article = get_an_article(0, article.id)
# Then
self.assertEqual(article.id, result_article.id)
self.assertEqual(title, result_article.title)
def test_it_should_raise_exception_when_article_does_not_exist(self) -> None:
# Given
invalid_article_id = 9988
# Expect
with self.assertRaises(Article.DoesNotExist):
# user.id는 상관없어서 그냥 0 넣었음
get_an_article(0, invalid_article_id)
def test_get_article_list_should_prefetch_like(self) -> None:
# Given
user = User.objects.create(name="test_user")
articles = [Article.object.create(title=f"{i}") for i in range(1, 21)]
do_like(user.id, articles[-1].id)
# When
with self.assertNumQueries(3):
result_articles = get_article_list(user.id, 0, 10)
result_counts = [a.like_set.count() for a in result_articles]
# Then
self.assertEqual(len(result_articles), 10)
self.assertEqual(1, result_counts[0])
self.assertEqual(
[a.id for a in reversed(articles[10:20])],
[a.id for a in result_articles],
)
def test_get_article_list_should_contain_my_likes_when_like_exists(self) -> None:
# Given
user = User.objects.create(name="test_user")
article1 = Article.objects.create(title="article1")
like = do_like(user.id, article1.id)
Article.objects.create(title="article2")
# When
articles = get_article_list(user.id, 0, 10)
# Then
self.assertEqual(like.id, articles[1].my_likes[0].id)
self.assertEqual(0, len(articles[0].my_likes))
def test_get_article_list_should_not_contain_my_likes_when_user_id_is_zero(self) -> None:
# Given
user = User.objects.create(name="test_user")
article1 = Article.objects.create(title="article1")
Like.objects.create(user_id=user.id, article_id=article1.id)
Article.objects.create(title="article2")
invalid_user_id = 0
# When
articles = get_article_list(invalid_user_id, 0, 10)
# Then
self.assertEqual(0, len(articles[1].my_likes))
self.assertEqual(0, len(articles[0].my_likes))
- ./test.sh 실행하기
09. ONDELETE CASCADE
sql의 cascade
CREATE TABLE temp_user (
id bigint AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL
);
CREATE TABLE temp_article (
id bigint AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL
);
CREATE TABLE temp_like(
id bigint AUTO_INCREMENT PRIMARY KEY,
user_id bigint NOT NULL,
article_id bigint NOT NULL,
constraint unique_user_article
unique (user_id, article_id),
constraint temp_like_article_id_fk_temp_article_id
foreign key (article_id) references temp_article (id) ON DELETE CASCADE,
constraint temp_like_user_id_fk_temp_user_id
foreign key (user_id) references temp_user (id) ON DELETE CASCADE
);
- foreign key에 ON DELETE CASCADE 옵션이 붙어있는 것을 확인할 수 있음.
- ON DELETE CASCADE: 부모 테이블 row가 삭제될 경우, 연결된 자식 테이블의 row도 삭제.
- ON DELETE SET NULL: 필드 nullable 설정 필요. not null 해제하고 저장.
django의 cascade
- 공식문서: https://docs.djangoproject.com/en/4.0/ref/models/fields/#django.db.models.CASCADE
- sql cascade 없어도 마치 있는 것처럼 relationship 객체를 삭제해준다.
- 앞에서 like 테이블 만들 때 models.CASCADE 사용했음.
- 그러나 실제 테이블 조회하면 CASCADE 옵션이 없음.
## test_cascade.py
from django.db import connection
from django.test import TestCase
from django.test.utils import CaptureQueriesContext
from tabom.models import Article, Like, User
class TestCascade(TestCase):
def test_capture_what_queries_excuted_when_cascade(self) -> None:
user = User.objects.create(name="user1")
article = Article.objects.create(title="artice1")
like = Like.objects.create(user_id=user.id, article_id=article.id)
with CaptureQueriesContext(connection) as ctx:
article.delete()
print(ctx)
10. 게시글 삭제
게시글 삭제 기능
## tests/test_you_can_delete_an_article(self) -> None:
def test_you_can_delete_an_article(self) -> None:
# Given
user = User.objects.create(name="user1")
article = Article.objects.create(title="artice1")
like = do_like(user.id, article.id)
# When
delete_an_article(article.id)
# Then
self.assertFalse(Article.objects.filter(id=article.id).exists())
self.assertFalse(Like.objects.filter(id=like.id).exists())
## services/article_service.py
def delete_an_article(article_id: int) -> None:
Article.objects.filter(id=article_id).delete()
- DoesNotExit가 아니라 exists()를 사용해서 삭제가 되었음을 검증했음.
- 테스트의 검증 용도로는 둘 중 어느 방법을 사용해도 무관함.
11. 게시글 생성
게시글 생성
## tests
def test_you_can_create_an_article(self) -> None:
# Given
title = "test_title"
# When
article = create_an_article(title)
# Then
self.assertEqual(article.title, title)
## services
def created_an_article(title: str) -> Article:
return Article.objects.create(title=title)
- 테스트에서 Article.objects.create() 모두 create_an_article() 호출로 변경함.
3주차 숙제
- 디자인 패턴에 대해 공부하기. 책 "헤드 퍼스트 디자인 패턴" 추천
- 디자인 패턴은 종류가 엄청 많음.
- 이 중에서 스트레티지 패턴, 스테이트 패턴, 옵저버 패턴, 싱글턴 패턴은 꼭 배워보기.
'스파르타코딩클럽 > 강의 정리' 카테고리의 다른 글
[스파르타] Django 심화반 2주차 (완강) (0) | 2022.03.08 |
---|---|
[스파르타] Django 심화반 1주차 (완강) (0) | 2022.03.08 |
[스파르타] Django 기초반 2주차 (완강) (0) | 2022.01.20 |
[스파르타] Django 기초반 1주차 (완강) (0) | 2022.01.19 |
[스파르타] 실전 머신러닝 적용 4주차 (2) | 2022.01.11 |
Comments