[Django] 리팩토링 적응기 2일차, api 제한기 1일차
어제 다 못한 리팩토링을 진행하면서 api 에 제한을 넣어볼 생각이다.
일단 어제 다 못한 리팩토링은 다음과 같다
fat model 적용하기
일단 fatmodel 부터 적용해보려고 한다
아주 간단하게 일단 model 에 함수를 넣어보려고 한다.
class User(AbstractUser, TimeStamp):
class SocialProvider(models.TextChoices):
GOOGLE = "GOOGLE"
KAKAO = "KAKAO"
NAVER = "NAVER"
username_validator = UnicodeUsernameValidator()
username = models.CharField(
_("username"),
max_length=150,
unique=True,
help_text=_(
"Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only." # noqa : E501
),
validators=[username_validator],
error_messages={
"unique": _("A user with that username already exists."),
},
)
password = models.CharField(_("password"), max_length=128, null=True)
social_provider = models.CharField(
max_length=30,
choices=SocialProvider.choices,
default=SocialProvider.GOOGLE,
)
is_subscribed = models.BooleanField(default=False)
@classmethod
def get_or_create_user(self, email):
try:
user = User.objects.get(username=email)
except User.DoesNotExist:
user = User.objects.create(username=email, password="")
send_email(
email,
"Welcome to join us",
welcome_email(user.username),
)
return user
일단 유저 모델에 잘 넣어주고 view 를 살짝 수정해주었는데 pytest 를 진행하니 다음과 같은 에러가 발생했다.

다음과 같이 뭔가 찾을 수 없다는 에러가 발생하는데 처음에는 코드의 문제라고 생각했는데 pytest 관련 문제인 것 같다.
그래서 테스트 코드를 수정해봤다.
수정 전 : @patch("accounts.views.send_email")
수정 후 : @patch("accounts.utils.send_email")
이 부분이였는데 views 에서 이제 import 를 받고 있지 않아서 발생한 문제였다.
근데 계속 pytest 에 testing 칸이 문제여서 해결했다..
이상하게 계속 todos 의 테스트만 테스트하고, 나머지는 뜨지도 않길래 뭔가하고 찾아보니
https://stackoverflow.com/questions/54387442/vs-code-not-finding-pytest-tests
VS Code not finding pytest tests
I have PyTest setup in vs-code but none of the tests are being found even though running pytest from the command line works fine. (I'm developing a Django app on Win10 using MiniConda and a Python...
stackoverflow.com
여기에서 두번째인 > python configuration 을 통해서 해결할 수 있었다. 그냥 파일을 다른 것을 선택해주면 아래와 같이 다른 폴더가 떴다. todos 로 고정되어 있었던 듯 했다.

그래서 pytest 가 실패한 것을 디버깅해보니..

저번과 같은 문제였는데 여기에서 Exception 난 사항이

이 부분에서도 반영되어서 바로 400에러가 떠버리는 문제가 있었다.
라고 생각했는데 디버깅을 진행해보니 propagation 문제가 아니라 그냥 send_email 에서 에러가 뜨는 문제였다 머쓱

이런 에러가 발생했는데 class 로 뭔가하면서 잘 안되는 듯 보인다...
그래서 물어보니
from dataclasses import dataclass, asdict
이걸 사용하라고 해서 해봤다.
email = resend.Emails.send(asdict(params))
이런식으로 보내도록 변환했다.
그랬더니 아래와 같은 문제가 발생했다.

보니까 class 를 잘못해서 from으로 바꾸려하니 keyword라 할 수 없다.

그래서 이런 코드를 아래와 같이 수정했다.


pytest 를 돌려보겠다.
해결되었다!!

이제...
두번째 일을 해본다!!
두번째 해야할 일은 api 호출에 약간의 제한을 넣어볼 예정이다.
근데 일단 후원한 사람과 후원하지 않은 사람들을 파악해야하기때문에
추가적으로 언제 마지막으로 사용한건지 파악하는 단계가 필요하다.
그러므로 테이블을 약간 수정해보는게 필요해보인다.

테이블을 따로 만드는 과정이 추가될 것 같다.
그리고 위의 그림에서 todo_id 는 굳이.. 필요할까 싶은데.. 흠... 고민이 된다..

is_premium 속성을 넣어주고

view 도 손을 봐줬다.
그리고 test 도 가능하도록 수정해주었다.
이렇게 일단 user 자체에서 premium 여부를 확인하는 것을 끝이 났고..
이제 view에서 확인해야한다.
대략적인 흐름도는 다음과 같다.
1. User 가 premium인지 확인한다.
2-1. premium인 경우, 생성 시간과 상관없이 생성하되, 시간 테이블의 timestamp 는 업데이트한다.
2-2. premium이 아닌 경우, 시간테이블에서 시간을 확인한후에 10초가 지났는지 여부를 파악한다.
2-3-1. 10초가 지나지 않았다면 400번대 에러를 발생한다. (프론트와 협의할 것)
2-3-2. 10초가 지난 상태라면 추천 하위 투두를 생성하고, 시간 테이블의 timestamp 를 업데이트한다.
이런 흐름으로 진행될 것 같다.
팀장님과 테스트 코드를 수정하다보니 이해못했던 testcode 를 이해하게 되었다.
@pytest.mark.django_db
class TestGoogleLogin:
@pytest.fixture
def api_client(self):
return APIClient()
@patch("accounts.views.id_token.verify_oauth2_token")
@patch("accounts.models.send_welcome_email")
def test_google_login_new_user(
self, mock_send_welcome_email, mock_verify_oauth2_token, api_client
):
# Mock the token verification response
mock_verify_oauth2_token.return_value = {
"iss": "accounts.google.com",
"email": "testuser@example.com",
}
# Define the URL for the GoogleLogin view
url = reverse("google_login")
# Create a mock request
data = {"token": "mock_token", "device_token": "mock_device_token"}
# Call the view
response = api_client.post(url, data, format="json")
# Check that the user was created
user = User.objects.get(username="testuser@example.com")
assert user is not None
# Check that the device was created
device = FCMDevice.objects.get(
user_id=user.id, registration_id="mock_device_token"
)
assert device is not None
# Check that the email was sent
mock_send_welcome_email.assert_called_once_with(
user.username,
user.username,
)
# Check the response status
assert response.status_code == status.HTTP_200_OK
assert "refresh" in response.data
assert "access" in response.data
일단 @patch 에서 아래에서 위 순서대로 함수에 넣어주어야 한다 따라서 send_email, verify 순서로 진행되는 것이고..
verify 의 경우에 return value 를 통해 무조건 저 값이 나오도록 한것이었다...
그리고 iss 값을 넣는 이유는
if "accounts.google.com" in idinfo["iss"]:
email = idinfo["email"]
user = User.get_or_create_user(email)
여기에서 통과하게 하기 위함이였다....
여튼 재밌었다.
다시 본론으로 돌아와서,

이런식으로 모델을 만들었다. (이름정하는데 무려 10분이나 걸렸다.)
이제 serializer 와 view 를 수정하는 식을 만들면 된다!!