Faker

  • 더미데이터를 만든다.

https://pypi.org/project/django-faker/

pip install Faker

 

from faker import Faker

faker = Faker()
# 이름에만 한글이 적용됨
faker = Faker("ko_KR")

print(faker.name())
print(faker.first_name())
print(faker.last_name())
print(faker.word())
print(faker.sentence())
print(faker.text())

 

 

Faker로 게시글 여러개 만들기

from articles.models import Article
from faker import Faker
# 그 밖에 import 안 한게 있다면 추가하기

class ArticleReadTest(APITestCase):
    @classmethod
    def setUpTestData(cls):
        cls.faker = Faker()
        cls.articles = []
        #10명의 유저가 게시글 작성
        for i in range(10):
            cls.user = User.objects.create_user(cls.faker.name(), cls.faker.word())
            cls.articles.append(
                Article.objects.create(
                title=cls.faker.sentence(),
                content=cls.faker.text(),
                user=cls.user,
                )
            )

 

 

 

 

(app - urls.py)

urlpatterns = [
    path('<int:pk>/', views.ArticleDetailView.as_view(),name='article_detail_view'),
]

 

(app - models.py)

from django.urls import reverse

class Article(models.Model):
    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)

    #추가하기
    def get_absolute_url(self):
        # 'pk'값으로 self.pk값을 kwargs에 넣어줌
        # article_detail_view에 pk 값을 넣고 그 url의 문자열을 가져옴
        return reverse('article_detail_view', kwargs={'pk':self.pk})

 

(app - views.py)

class ArticleDetailView(APIView):
    def get(self, request, pk):
        article = ArticleModel.objects.get(pk=pk)
        serializer = ArticleSerializer(article)
        return Response(serializer.data)

 

(app - test.py)

from articles.serializers import ArticleSerializer


class ArticleReadTest(APITestCase):
    def test_get_article(self):
        for article in self.articles:
            # 함수의 return 값으로 url 값을 가져옴
            url = article.get_absolute_url()
            response = self.client.get(url)
            serialize = ArticleSerializer(article).data

            #items : key와 value를 뽑아내고 싶을 때 사용
            for k, v in serialize.items():
                # response.data[k]는 reponse의 value값
                self.assertEqual(response.data[k], v)
                print(k, v)
            
            #위와 똑같음
            #self.assertEqual(article.title, response.data['title'])

 

 

(app - models.py)  유저를 이름으로 보여주기

class ArticleSerializer(serializers.ModelSerializer):
    user = serializers.SerializerMethodField()

    # get + 언더바(_) + serializers.SerializerMethodField()
    def get_user(self, obj):
        return obj.user.username

 

'Python > Django' 카테고리의 다른 글

[DRF] 16. setUpTestData  (0) 2023.04.30
[DRF] 15. setUp, @classmethod, @staticmethod  (0) 2023.04.27
[DRF] 14.5 테스트 코드 종류  (0) 2023.04.27
[DRF] 14. 테스트 코드란?  (0) 2023.04.27
[DRF] 13. 개인페이지 (Q객체, F객체)  (0) 2023.04.27

setUpTestData

  • setUpTestData : 생성된 데이터베이스 픽스쳐는 모든 테스트 메소드에서 사용할 수 있다.
  • setUP: 각각의 테스트 메소드가 실행되기 전에 실행되는 메소드
class ArticleCreateTest(APITestCase):
    @classmethod
    def setUpTestData(cls):
        cls.user_data = {'username':'John', 'password':'password'}
        cls.article_data = {'title':'테스트', 'content':'내용 테스트'}
        cls.user = User.objects.create_user('John', 'password')
        #토큰을 가져올 때 cls.client로 쓰면 에러남

    def setUp(self):
        self.access_token = self.client.post(reverse('token_obtain_pair'), self.user_data).data['access']

 

 

로그인 하지 않고, 로그인 하고 글작성하기

    def test_fail_if_not_logged_in(self):
        url = reverse("article_view")
        response = self.client.post(url, self.article_data)
        self.assertEqual(response.status_code, 401)
        
        
    def test_fail_if_not_logged_in(self):
        response = self.client.post(
            path=reverse("article_view"),
            data=self.article_data,
            HTTP_AUTHORIZATION = f'Bearer {self.access_token}'
        )
        self.assertEqual(response.data["message"], "글 작성 완료!!")

 

 

 

 

이미지 게시글

# 이미지 업로드 및 임시 이미지파일
from django.test.client import MULTIPART_CONTENT, encode_multipart, BOUNDARY
from PIL import Image
import tempfile

# 임시 파일을 만들어 임시 이미지를 만듬

def get_temporary_image(temp_file):
    size = (200, 200)
    color = (255, 0, 0, 0)
    image = Image.new("RGBA", size, color)
    image.save(temp_file, 'png')
    return temp_file
    
    
    
    # 얘는 article class 아래 만들어야 함
    def test_create_article_with_image(self):
        # 임시 이미지 생성
        temp_file = tempfile.NamedTemporaryFile()
        temp_file.name = 'image.png'
        image_file = get_temporary_image(temp_file)
        # 파일 포인터를 이미지 파일의 첫 번째 프레임의 시작 위치로 이동시킴
        image_file.seek(0)
        self.article_data['image'] = image_file

    
        # 이미지 전송
        response = self.client.post(
            path=reverse('article_view'),
            data=encode_multipart(data=self.article_data, boundary=BOUNDARY),
            content_type=MULTIPART_CONTENT,
            HTTP_AUTHORIZATION=f'Bearer {self.access_token}'
        )
        self.assertEqual(response.data['message'], '글 작성 완료!!')

 

 

'Python > Django' 카테고리의 다른 글

[DRF] 17. Faker  (0) 2023.04.30
[DRF] 15. setUp, @classmethod, @staticmethod  (0) 2023.04.27
[DRF] 14.5 테스트 코드 종류  (0) 2023.04.27
[DRF] 14. 테스트 코드란?  (0) 2023.04.27
[DRF] 13. 개인페이지 (Q객체, F객체)  (0) 2023.04.27

setUp

 

  • setUp : 모든 test 메소드 이전에 실행한다.
  • tearDown : 모든 test 메소드 마지막에 실행한다.

 

class LoginUserTest(APITestCase):
    def setUp(self):
        self.data = {'username':'John', 'password':'password'}
        # UserManager의 create_user 함수
        self.user = User.objects.create_user('John', 'password')

    def test_login(self):
        response = self.client.post(reverse('token_obtain_pair'), self.data)
        self.assertEqual(response.status_code, 200)
    
    def test_get_user_data(self):
        access_token = self.client.post(reverse('token_obtain_pair'), self.data).data['access']
        # 토큰이 get으로 돌아옴
        response = self.client.get(
            path=reverse('user_view'),
            # 헤더에 AUTHORIZATION 담아서 bearer에 토큰 보내줌
            HTTP_AUTHORIZATION=f'Bearer {access_token}'
        )
        # 가입한 username이 로그인한 username과 동일한지
        self.assertEqual(response.data['username'], self.data['username'])

 

 

※ 여기서 알아야 할 것 ※

access토큰을 받아와 AUTHORIZATION 헤더에 넣어주는 방법에 대해 알아야 합니다.

 

 

 

@classmethod (팩토리 메소드)

  • 정적 메소드 (클래스에서 직접 접근할 수 있는 메소드)

         * 파이썬에서는 정적 메소드임에도 인스턴스에 접근이 가능하다.

  • 인스턴스를 생성하지 않고 클래스 자체에 대해 작업할 수 있다.
  • 첫 번째 인자로 클래스를 받는다.
from datetime import date

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @classmethod
    # 첫번째 인자는 본인 클래스
    def fromBirthYear(cls, name, birthYear):
        # cls = class Person
        return cls(name, date.today().year - birthYear)
    
    def display(self):
        print(self.name + "'s age is: " + str(self.age))


person = Person("Adam", 19)
person.display()

# 인스턴스가 없어도 classmethod는 바로 실행할 수 있음
person1 = Person.fromBirthYear('John', 1985)
person1.display()

 

 

 

@staticmethod

  • 정적 메소드
  • 추가되는 인자가 없다.
  • 단순히 편리를 위한 메소드이다.
from datetime import date

# 클래스 밖에서 선언하나 staticmethod로 선언하나 차이가 없다
def isAdult(age):
        return age > 18

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    @staticmethod
    def isAdult(age):
        return age > 18

 

'Python > Django' 카테고리의 다른 글

[DRF] 17. Faker  (0) 2023.04.30
[DRF] 16. setUpTestData  (0) 2023.04.30
[DRF] 14.5 테스트 코드 종류  (0) 2023.04.27
[DRF] 14. 테스트 코드란?  (0) 2023.04.27
[DRF] 13. 개인페이지 (Q객체, F객체)  (0) 2023.04.27
 

테스트 종류

 

단위 테스트(Unit Test)

  • 소프트웨어의 각 기능이나 모듈이 의도한 대로 동작하는지를 검증하는 테스트
  • 소프트웨어 변경 시 예상치 못한 부작용을 최소화
  • 코드의 작은 부분(함수, 메서드, 클래스 등)을 개별적으로 검증
  • 빠른 시간 안에 수행할 수 있어 개발 과정에서 가장 많이 사용됨
  • 대표적인 단위 테스트 프레임워크로는 JUnit, pytest, unittest 등이 있음
  • 모든 테스트가 서로 독립적(sateless)

 

통합 테스트(Integration Test)

  • 각 기능이 모두 연결되어 잘 작동하는지를 검증하는 테스트
  • 각 모듈이 단위 테스트에서 검증된 후, 모듈들 간의 상호작용을 검증함
  • 대표적으로 스프링 프레임워크에서 사용되는 스프링 부트 테스트가 있음

 

 

E2E(End-to-End) 테스트

  • 전체 시스템을 대상(DB, 서버, 클라이언트 등)으로 수행하는 테스트
  • 사용자의 관점에서 시스템이 어떻게 동작하는지를 검증
  • 사용자가 시스템을 사용할 때 발생할 수 있는 문제를 대상으로 검증
  • 자동화가 어려우므로 많은 시간과 비용이 소요

 

 

 

통합 테스트와 E2E 테스트 차이

통합 테스트 E2E 테스트
개별 모듈의 동작 확인에 중점 사용자 경험에 초점
모듈 간의 상호작용을 검증 전체 시스템을 대상으로 수행

 

 

 

시스템 테스트(System Test)

  • 전체 시스템의 기능, 성능, 안전성을 검증
  • E2E 테스트를 포괄하는 테스트
  • 시스템이 예상한대로 동작하며 사용자 요구사항을 충족시키는지를 확인함
  • 수행 방식에는 수동 테스트와 자동화된 테스트가 있음

'Python > Django' 카테고리의 다른 글

[DRF] 16. setUpTestData  (0) 2023.04.30
[DRF] 15. setUp, @classmethod, @staticmethod  (0) 2023.04.27
[DRF] 14. 테스트 코드란?  (0) 2023.04.27
[DRF] 13. 개인페이지 (Q객체, F객체)  (0) 2023.04.27
[DRF] 12. 좋아요, 팔로우  (1) 2023.04.26

테스트 코드란?

    작성한 코드들이 원하는 값을 주는지 확인하는 코드

 

 

사용하는 이유

    1.버그를 쉽고 빠르게 찾을 수 있다.

    2. 코드가 얼마나 안전한지 확인할 수 있다.

    3. 코드의 복잡도를 낮출 수 있다.

    4. extreme programming (XP) 개념에서 중요하다.

          XP는 테스트 주도 개발(TDD) 방법을 사용한다. 지속적 통합(CI)과 지속적 배포(CD)를 지원하여 개발자들이 코드 변경 사항을 빠르게 수정할 수 있도록 하는데 목적이 있다.

    5. documentation화 제공한다.

          타인이 코드의 동작을 쉽게 이해하고 코드를 변경하면 문서화를 자동으로 업데이트 할 수 있다.

    6. performance(성능)를 체크한다.

          (ex, 시간이 얼마나 걸리는지)

 

 

 

 

 

Test Driven Development (테스트 주도 개발) 순서

   실패하는  테스트 코드 ->  테스트 코드를 성공시키기 위한 코드 작성 ->  refactoring (효율, 가독성)

 

 

사용 예시

py manage.py test <앱 이름>
# 앱 이름이 없으면 전체 test 코드를 실행
# 앱 이름이 있으면 앱의 test 코드만 실행

 

 

(app - test.py)

from django.test import TestCase


class TestView(TestCase):
    def test_two_is_three(self):
        # 2와 3이 같은지 확인
        self.assertEqual(2, 3)

 

 

https://www.django-rest-framework.org/api-guide/testing/

https://docs.djangoproject.com/en/4.2/topics/testing/overview/

https://docs.python.org/3/library/unittest.html#module-unittest

 

 

 

로그인 테스트 - 1

(app - test.py)

from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase

class UserRegistrationTest(APITestCase):
    def test_registration(self):
        # reverse : 해당 URL 패턴을 역으로 해석하여 URL 문자열을 반환하는 함수
        url = reverse('user_view')
        user_data = {
            "username" : "testuser",
            "fullname" : "테스터",
            "email" : "test@testuser.com",
            "password" : "password",
        }
        # 클라이언트로 post를 보냄
        # url로 user_data를 보냄
        response = self.client.post(url, user_data)
        # status 코드가 200인지 확인
        print(response.data)
        self.assertEqual(response.status_code, 200)

    def test_login(self):
        url = reverse('token_obtain_pair')
        user_data = {
            "username" : "testuser",
            "fullname" : "테스터",
            "email" : "test@testuser.com",
            "password" : "password",
        }
        response = self.client.post(url, user_data)
        print(response.data)
        self.assertEqual(response.status_code, 200)

    #{'detail': ErrorDetail(string='No active account found with the given credentials', code='no_active_account')}

 위에서 유저를 만들었는데 밑에서 로그인하니 에러가 난다.

 

이유

  • test 의 메소드를 실행할 때마다 DB 데이터를 초기화한다.
  • 테스트 코드의 실행 순서는 보장 되지 않는다.

전체 게시글 보기 수정

  • SerializerMethodField 사용 방법

 

https://www.django-rest-framework.org/api-guide/relations/

 

 

(app - serializers.py) 수정/추가 하기

class TodoListSerialize(serializers.ModelSerializer):
    user = serializers.SerializerMethodField()
    like_count = serializers.SerializerMethodField()
    comment_count = serializers.SerializerMethodField()

    def get_user(self, obj):
        return obj.user.email
    
    def get_like_count(self, obj):
        return obj.like.count()
    
    def get_comment_count(self, obj):
        return obj.comment_set.count()
    
    class Meta:
        model = Todos
        fields = ('title', 'user', 'comment_count', 'like_count')

 

 

 

프로필 페이지

  • 시리얼라이즈의 StringRelatedField 필드
  • related_name 혹은 set으로 참조

(app - urls.py)

urlpatterns = [
    path('<int:user_id>/profile/', views.ProfileView.as_view(), name='profile_view'),
]

 

(app - serializers.py)

class UserProfileSerializer(serializers.ModelSerializer):
    # 해당 모델의 __str__() 메서드를 호출하여 문자열로 표현된 관련 객체를 반환
    # PrimaryKeyRelatedField 는 아이디로 보여줌 // read_only=True
    follows = serializers.StringRelatedField(many=True)
    following = serializers.StringRelatedField(many=True)
    #related_name이나 set으로 참조
    todos_set = TodoListSerialize(many=True)
    likey = TodoListSerialize(many=True)

    class Meta:
        model = Users
        fields = ('id', 'email', 'follows', 'following', 'todos_set', 'likey')

 

(app - views.py)

class ProfileView (APIView):
    def get (self, request, user_id):
        user = get_object_or_404(Users, id=user_id)
        serialize = UserProfileSerializer(user)
        return Response(serialize.data)

 

 

 

Feed 페이지

  • Q 객체를 사용해 쿼리를 조작할 수 있다.

(app - urls.py)

urlpatterns = [
    path('feed/', views.FeedView.as_view(), name='feed_view'),
    ]

 

 

(app - views.py)

from django.db.models.query_utils import Q

class FeedView (APIView):
    permission_classes = [IsAuthenticated]

    def get (self, request):
        # Q 객체를 사용하여 쿼리 조건을 생성
        q = Q()
        for user in request.user.follows.all():
            # add 메서드를 사용해 Q 객체를 하나씩 추가
            # 팔로우가 비대칭이므로(한쪽만 팔로우 할 경우) OR를 조건으로 Todos 모델 인스턴스를 가져옴
            q.add(Q(user=user),q.OR)
        feeds = Todos.objects.filter(q)
        serialize = TodoListSerialize(feeds, many =True) 
        return Response(serialize.data)

 

 

 

 

 

 

Q 객체

  • 주로 filter( )와 함께 사용한다.
  • 복잡한 쿼리를 처리할 때 Q 객체를 사용한다.
  • Q 객체는 and( & ), or ( | ), xor ( ^ ) 등의 비교연산자와 함께 쓰인다.
  • 장고 ORM 에서 쿼리문처럼 사용한다.

         *장고 ORM 이란? 객체와 DB 데이터를 매핑(Mapping) 하는 역할. 즉,  SQL 쿼리를 작성하지 않고 장고에서 자동으로 SQL문을 생성하는 것을 말한다. 장점으로는 SQL문 문법이 통일성을 갖게 된다는 점이 있다.

from django.db.models import Q

Q(question__startswith="Who") | ~Q(pub_date__year=2005

https://docs.djangoproject.com/en/4.2/ref/models/querysets/#django.db.models.Q

 

 

 

 

 

F 객체

  • 모델의 필드 또는 어노테이트 될 열의 값(F 객체를 사용해 계산한 결과 값)을 나타낸다.
  • 연산을 할 때 사용한다.
  • 연산에 해당하는 쿼리를 생성한다.
  • 데이터베이스에서 연산을 처리한다.

※ 모델을 저장한 후에도 save( )를 실행할 때 마다 적용된다.

from django.db.models import F

reporter = Reporters.objects.get(name="Tintin")
reporter.stories_filed = F("stories_filed") + 1
reporter.save()

https://docs.djangoproject.com/en/4.2/ref/models/expressions/#django.db.models.F

'Python > Django' 카테고리의 다른 글

[DRF] 14.5 테스트 코드 종류  (0) 2023.04.27
[DRF] 14. 테스트 코드란?  (0) 2023.04.27
[DRF] 12. 좋아요, 팔로우  (1) 2023.04.26
[DRF] 11. 댓글 API(두 개의 모델 참조)  (0) 2023.04.26
[DRF] 10. 게시글 API  (0) 2023.04.26

좋아요 / Todo Done

  • Todos 모델의 like에 로그인한 유저가 없으면 저장, 있으면 제거합니다.
  • Todos 모델의 is_complete 필드를 True로 바꾼다.

 

(app - models.py) 추가하기

class Todos(models.Model):
    like = models.ManyToManyField(Users, related_name='likey')

 

(app - urls.py)

urlpatterns = [
    path('<int:todo_id>/like/', views.LikeView.as_view(), name='like_this'),
    path('<int:todo_id>/done/', views.DoneView.as_view(), name='done_this'),
]

 

(app - views.py)

class LikeView(APIView):
    def post(self, request, todo_id):
        todo = get_object_or_404(Todos, id=todo_id)
        print(todo.like)
        if request.user in todo.like.all():
            todo.like.remove(request.user)
            return Response('헤어져요', status=status.HTTP_200_OK)
        else:
            todo.like.add(request.user)
            return Response('좋아요', status=status.HTTP_200_OK)
    
class DoneView(APIView):
    def post(self, request, todo_id):
        todo = get_object_or_404(Todos, id=todo_id)
        if request.user == todo.user:
            todo.is_complete = True
            todo.save()
            return Response('할 일 완료', status=status.HTTP_200_OK)
        else:
            return Response({'message':'권한이 없습니다'},status=status.HTTP_400_BAD_REQUEST)

 

 

 

 

 

 

팔로우

  • 자신은 팔로우 하지 않는다.
  • 팔로우, 언팔로우 모두 같은 모델을 참조해야한다.

(app - urls.py)

urlpatterns = [
    path('<int:user_id>/follow/', views.FollowView.as_view(), name='follow_view'),
]

 

(app - models.py)

    # 한쪽이 팔로우 하면 다른 한쪽도 팔로우를 걸지 않도록
    # 대칭(symmetrical) 설정을 False로 만듬
    follows = models.ManyToManyField('self', symmetrical=False, related_name='following', blank=True)

 

(app - views.py)

class FollowView(APIView):
    def post(self, request, user_id):
        other = get_object_or_404(Users, id=user_id)
        me = request.user
        # 팔로우 하려는 사람이 내가 아닐 때
        if me != other:
            if other not in me.follows.all():
                me.follows.add(other)
                return Response('팔로우', status=status.HTTP_200_OK)
            else:
                me.follows.remove(other)
                return Response('언팔로우', status=status.HTTP_200_OK)
        else:
            return Response({'message':'나를 팔로우 할 수 없습니다'}, status=status.HTTP_400_BAD_REQUEST)

 

 

댓글 API

  • url로 보낸 id 값을 받아야 한다.
  • 댓글을 조회하면 user는 email
  • todo는 title을 보여준다.

 

 

(app - models.py) 추가하기

class Comment(models.Model):
    user = models.ForeignKey(Users, on_delete=models.CASCADE)
    a_todo = models.ForeignKey(Todos, on_delete=models.CASCADE)
    content = models.CharField(max_length=256)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return str(self.content)

 

 

(app - urls.py) 추가하기

urlpatterns = [
    path('<int:todo_id>/comment/', views.CommentView.as_view(), name='comment_view'),
    path('<int:todo_id>/comment/<int:comment_id>/', views.CommentDetailView.as_view(), name='comment_detail_view'),
]

 

 

(app - serializers.py)

class CommentSerialize(serializers.ModelSerializer):
    #시리얼라이저에서 메서드를 호출하여 필드의 값을 결정한다
    user = serializers.SerializerMethodField()
    todo = serializers.SerializerMethodField()
    
    #여기 메서드 호출
    def get_user(self, obj):
        return obj.user.email
    
    def get_todo(self, obj):
        return obj.a_todo.title
    
    class Meta:
        model = Comment
        # a_todo를 뺀 나머지 필드
        exclude = ('a_todo', )

class CommentCreateSerialize(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = ['content', ]


class TodoSerialize(serializers.ModelSerializer):
    # 추가하기
    # 역참조
    # todo 게시글에서도 comment를 보여줌
    comment_set = CommentSerialize(many = True)

 

 

(app - views.py)

class CommentView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, todo_id):
        comment = Comment.objects.all()
        serialize = CommentSerialize(comment, many=True)
        return Response(serialize.data, status=status.HTTP_200_OK)
    
    def post(self, request, todo_id):
        serialize = CommentCreateSerialize(data=request.data)
        todo = Todos(id=todo_id)
        if serialize.is_valid():
            serialize.save(user=request.user, a_todo=todo)
            return Response(serialize.data, status=status.HTTP_201_CREATED)
        else:
            return Response(f'${serialize.errors}', status=status.HTTP_400_BAD_REQUEST)

class CommentDetailView(APIView):
    permission_classes = [IsAuthenticated]

    def put(self, request, todo_id, comment_id):
        comment = get_object_or_404(Comment, id=comment_id)
        if request.user == comment.user:
            serialize = CommentSerialize(comment, data=request.data)
            if serialize.is_valid():
                serialize.save()
                return Response({"수정됨":serialize.data}, status=status.HTTP_200_OK)
        else:
            return Response({'message':'권한이 없습니다'},status=status.HTTP_400_BAD_REQUEST)
    
    def delete(self, request, todo_id, comment_id):
        comment = get_object_or_404(Comment, id=comment_id)
        if request.user == comment.user:
            comment.delete()
            return Response('댓글 삭제 완료', status=status.HTTP_204_NO_CONTENT)
        else:
            return Response({'message':'권한이 없습니다'},status=status.HTTP_400_BAD_REQUEST)

 

 

Todo 리스트

  • 게시글을 전부 불러와 보여질 페이지
  • 여러개의 시리얼라이즈를 불러올 때 many=True를 사용

(app - models.py )

from django.db import models
from user.models import Users


class Todos(models.Model):
    user = models.ForeignKey(Users, on_delete=models.CASCADE)
    title = models.CharField(max_length=50)
    # 파일 찾기 기능을 고려해 시간대별 저장
    image = models.ImageField(blank=True, upload_to='%Y/%m/%d/')
    is_complete = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    completion_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return str(self.title)

 

(app - views.py)

from rest_framework.permissions import IsAuthenticated
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from todo.models import Todos      
from todo.serializers import TodoSerialize


class TodoView(APIView):
    # 로그인한 유저인지 확인
    permission_classes = [IsAuthenticated]
    def get(self, request):
        todo = Todos.objects.all()
        # 여러개의 시리얼라이즈를 불러올 때 many=True를 사용
        serialize = TodoSerialize(todo, many=True)
        return Response(serialize.data, status=status.HTTP_200_OK)

 

(app - serializers.py)

from rest_framework import serializers
from todo.models import Todos

class TodoSerialize(serializers.ModelSerializer):
    class Meta:
        model = Todos
        fields = '__all__'

 

 

 

 

 

 

Todo 생성

  • 게시글 작성은 title과 image 데이터만 받음
  • user는 데이터를 실어온 시리얼라이즈에 로그인한 유저의 정보를 추가하여 저장

 

(app - views.py)

from todo.serializers import TodoCreateSerialize

class TodoView(APIView):
    def post(self, request):
        serialize = TodoCreateSerialize(data=request.data)
        if serialize.is_valid():
            # user를 저장하지 않으면 에러남
            serialize.validated_data['user'] = request.user
            serialize.save()
            # 또는 아래와 같이 유저를 저장할 수 있다.
            # serialize.save(user=request.user)
            return Response(serialize.data, status=status.HTTP_201_CREATED)
        else:
            return Response({'message':f'${serialize.errors}'}, status=status.HTTP_400_BAD_REQUEST)

 

(app - serializers.py)

class TodoCreateSerialize(serializers.ModelSerializer):
    class Meta:
        model = Todos
        fields = ['title', 'image']

 

https://www.django-rest-framework.org/api-guide/fields/

 

 

 

 

 

Todo 수정, 삭제하기

  • 개인페이지의 열람, 수정, 삭제는 로그인한 유저와 게시글을 쓴 유저의 정보가 일치할 때만 가능
  • 객체가 존재하지 않으면 404 에러를 발생

 

(app - views.py)

class TodoDetailView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, todo_id):
        # 객체가 존재하지 않으면 404에러를 발생
        todo = get_object_or_404(Todos, id=todo_id)
        if request.user == todo.user:
            serialize = TodoSerialize(todo)
            return Response(serialize.data, status=status.HTTP_200_OK)
        else:
            return Response({'message':'권한이 없습니다.'}, status=status.HTTP_400_BAD_REQUEST)
    
    def put(self, request, todo_id):
        todo = get_object_or_404(Todos, id=todo_id)
        if request.user == todo.user:
            serialize = TodoCreateSerialize(todo, data=request.data)
            if serialize.is_valid():
                serialize.save()
                return Response(serialize.data, status=status.HTTP_200_OK)
            else:
                return Response({'message':'데이터가 옳바르지 않습니다.'}, status=status.HTTP_400_BAD_REQUEST)
        else:
            return Response({'message':'권한이 없습니다.'}, status=status.HTTP_400_BAD_REQUEST)   
    
    def delete(self, request, todo_id):
        todo = get_object_or_404(Todos, id=todo_id)
        if request.user == todo.user:
            todo.delete()
            return Response({'message':'삭제 완료'},status=status.HTTP_204_NO_CONTENT)
        else:
            return Response({'message':'권한이 없습니다.'}, status=status.HTTP_400_BAD_REQUEST)

 

 

 

 

 

 

 

 

+) 시리얼라이즈를 활용( get_user 메소드 )하여 user를 보여줄 때 email로 보여줄 수 있다.

from rest_framework import serializers
from todo.models import Todos


class TodoSerialize(serializers.ModelSerializer):
    user = serializers.SerializerMethodField()

    # user attribute(변수) 앞에 get을 사용
    # obj는 해당 Article
    def get_user(self, obj):
        # return 값이 user라는 attribute 값에 들어감
        return obj.user.email
        
    class Meta:
        model = Todos
        fields = '__all__'

 

스태틱, 미디어 폴더

  • 장고에서 스태틱, 미디어 폴더를 사용하기 위해서는 Pillow를 설치해야 한다.
  • 미디어 폴더의 경우 .gitignore에 등록하면 깃에 업로드 되는 것을 피할 수 있다.
  • runserver(개발환경)에서 media 파일 서빙을 권장하지 않는다.
  • 이 때문에 urlpatterns을 작성하면 파일이 서빙된다.

 

https://docs.djangoproject.com/en/4.2/howto/static-files/

 

 

pip install Pillow

 

 

(settings.py)

STATIC_ROOT = BASE_DIR / 'static'
STATIC_URL = '/static/'

MEDIA_ROOT = BASE_DIR / 'media'
MEDIA_URL = '/media/'

 

 

(urls.py)

from django.conf import settings
from django.conf.urls.static import static

urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

 

 

 

 

 

 이미지 등록하기

  • postman에서 이미지 등록시 form-data를 사용

 

 

 

 

 

 

 

 

 

 

 

 

+) access 토큰을 enviroments에 등록하여 쓸 수 있다.

        *단, 처음 작성할 때와 달리 Current value가 자동으로 변경되지 않으므로, Initial value, Current value  둘 다 입력해야한다.

'Python > Django' 카테고리의 다른 글

[DRF] 11. 댓글 API(두 개의 모델 참조)  (0) 2023.04.26
[DRF] 10. 게시글 API  (0) 2023.04.26
[DRF] 08.5 토큰 주기설정, permission  (0) 2023.04.26
[DRF] 08. Payload 커스텀  (0) 2023.04.26
[DRF] 07. 유저 API  (0) 2023.04.26

+ Recent posts