ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Django 3일차 - Rest framework
    Django/OZ 2024. 3. 9. 22:51

    API (Application Programming Interface)

    • 프로그램간의 상호작용(데이터 교환)
    • 애플리케이션 간에 데이터를 교환하거나 서비스를 공유하기 위한 인터페이스
    • 즉, 프로그래머가 다른 소프트웨어나 서비스를 활용하기 위해 사용하는 메서드와 프로토콜의 집합을 의미

     

    REST API

    • 자원에 대한 URL(Uniform Resource Locator)을 사용하여 자원을 나타내며 
    • HTTP 메서드(GET, POST, PUT, DELETE 등)를 사용하여 해당 자원에 대한 작업을 수행

     

    RESTful API

    • REST 아키텍처를 따르는 API
    • URL주소와 메서드만 보고 API의 역할을 알 수 있다는 것이 가장 큰 특징
    • REST의 원칙을 준수함으로써 일관성 있고 표준화된 인터페이스를 제공하여 사용자가 이해하기 쉽고 유연한 API를 구축할 수 있도록 돕는다.

     

     

    API는 소프트웨어 간의 상호작용을 위한 인터페이스를 의미하며, REST API는 웹 기반의 API 디자인 아키텍처 스타일 중 하나이고, RESTful API는 REST의 원칙을 준수하여 설계된 API를 의미

     

     

    가상환경 접속 확인

    poetry shell 하면 경로가 (oz-django-py3.12) 이 뜨는데 이게 가상환경

    (.venv) (base) hotbari@yeonsuui-MacBookPro oz-Django % poetry shell
    Spawning shell within /Users/hotbari/Desktop/BE_02_김연수/oz-Django/.venv
    수/oz-Django/.venv/bin/activate'"'"''
    (oz-django-py3.12) (base) hotbari@yeonsuui-MacBookPro oz-Django % exit

     

    Djangorestframework 설치

     

    rest_framework 설치 내용을 Django에게 알려준다

    앱이 너무 많아서 장고에서 기본적으로 설치된 앱(DJANGO_SYSTEM_APPS)과

    직접 생성한 앱(CUSTOM_USER_APPS)을 구분해서 관리해준다.

    ## config/settings.py
    
    # Application definition
    DJANGO_SYSTEM_APPS=[
        "django.contrib.admin",
        "django.contrib.auth",
        "django.contrib.contenttypes",
        "django.contrib.sessions",
        "django.contrib.messages",
        "django.contrib.staticfiles",
    ]
    CUSTOM_USER_APPS = [
        "users.apps.UsersConfig",
        "boards.apps.BoardsConfig",
        "common.apps.CommonConfig",
        "rest_framework"
    ]
    
    # []없이!
    INSTALLED_APPS = CUSTOM_USER_APPS + DJANGO_SYSTEM_APPS

     


    Feeds API 생성

    (1) 전체 피드 데이터를 불러오는 GET/feed

    ## feeds/models.py
    
    from django.db import models
    
    #CommonModel을 상속받기위해 불러옴
    from common.models import CommonModel
    
    # Create your models here.
    # 제목(title), 내용(content), 작성자(User)
    # User:Feed = 1:N 
    # Feed가 User를 FK로 가짐
    
    class Feed(CommonModel):
        title = models.CharField(max_length=30)
        content = models.CharField(max_length=120)
        
        # User가 삭제되면 Feed도 삭제되도록 옵션 설정
        user = models.ForeignKey("users.User", on_delete=models.CASCADE)

     

    ## feeds/views.py
    
    from django.shortcuts import render
    # RestFramework에서 제공하는 APIView를 사용
    from rest_framework.views import APIView
    from .models import Feed
    
    # Create your views here.
    class Feeds(APIView):
        # 전체 게시글 조회
        def get(self, request):
            # 전체 객체데이터를 가짐
            feeds = Feed.objects.all()
            
            # 객체데이터를 JSON데이터 형태로 시리얼 라이즈하기 위해 serializers.py 생성

     

    Serializer
    장고의 객체 데이터를 JSON 데이터로, 또 그 반대로도 변환해준다. ModelSerializer을 상속받아 만듬.
    ## feeds/serializers.py
    
    from rest_framework.serializers import ModelSerializer
    from .models import Feed
    
    # ModelSerializer는 직렬화를 해주는 역할
    class FeedSerializer(ModelSerializer):
        # 직렬할 것에 대한 특징
        class Meta:
            model = Feed
            fields = "__all__"

     

    feed/views.py 이어서 코드 작성

    ## feeds/views.py
    
    from django.shortcuts import render
    # RestFramework에서 제공하는 APIView를 사용
    from rest_framework.views import APIView
    from .models import Feed
    # 만든 시리얼라이즈 import
    from .serializers import FeedSerializer
    from rest_framework.response import Response
    
    # Create your views here.
    class Feeds(APIView):
        # 전체 게시글 조회
        def get(self, request):
            # 전체 객체데이터를 가짐
            feeds = Feed.objects.all()
            
            # 객체 -> JSON (시리얼라이즈)
            # ModelSerializer가 상속된 FeedSerializer 클래스가 다수의 feeds객체를 직렬화 할것이라는 의미
            serializer = FeedSerializer(feeds, many=True)
            # 직렬화된 데이터를 반환
            return Response(serializer.data)

     

    뷰를 만들었으니 url 맵핑이 필요하다.

    *config/urls.py에 작성하는 것이 아닌 feeds/urls.py이다.

    ## feeds/urls.py
    
    from django.urls import path
    from . import views
    
    urlpatterns = [
        # 전체게시글 조회이기때문에 루트경로로 입력
        # .as_view()는 APIView와 url을 맵핑시켜주는 함수
        path("",views.Feeds.as_view())
    ]

     

     

     

    FeedsConfig를 등록

    ## config/settings.py
    
    CUSTOM_USER_APPS = [
        "users.apps.UsersConfig",
        "boards.apps.BoardsConfig",
        "common.apps.CommonConfig",
        "feeds.apps.FeedsConfig",
        "rest_framework"
    ]

     

    아직 만들지않은 users.urls는 잠시 주석처리 해준다.

    ## config/urls.py
    
    urlpatterns = [
        path("admin/", admin.site.urls),
        path("api/v1/feeds/", include("feeds.urls")), #feeds 앱의 API 버전1
        #path("api/v1/user/", include("users.urls")), #뒤에 슬래시 까먹지 마세요잉
        #path("api/v1/reviews/", include("reviews.urls")),
    ]

     

    ## feeds/admin.py
    
    from django.contrib import admin
    from .models import Feed
    
    # Register your models here.
    @admin.register(Feed)
    class FeedAdmin(admin.ModelAdmin):
        pass

    http://127.0.0.1:8000/api/v1/feeds

     

     

    (2) 특정 피드데이터를 불러오는 Get/feeds/1

    ## feeds/views.py
    
    from django.shortcuts import render
    # RestFramework에서 제공하는 APIView를 사용
    from rest_framework.views import APIView
    from .models import Feed
    # 만든 시리얼라이즈 import
    from .serializers import FeedSerializer
    from rest_framework.response import Response
    # NotFound import
    from rest_framework.exceptions import NotFound
    
    # Create your views here.
    class Feeds(APIView):
        # 전체 게시글 조회
        def get(self, request):
            # 전체 객체데이터를 가짐
            feeds = Feed.objects.all()
            
            # 객체 -> JSON (시리얼라이즈)
            # ModelSerializer가 상속된 FeedSerializer 클래스가 다수의 feeds객체를 직렬화 할것이라는 의미
            serializer = FeedSerializer(feeds, many=True)
            # 직렬화된 데이터를 반환
            return Response(serializer.data)
            
            
    class FeedDetail(APIView):
        def get_object(self, feed_id):
            try:
                return Feed.objects.get(id=feed_id)
            # feed가 존재하지 않을 경우
            except Feed.DoesNotExist :
                raise NotFound
        
        def get(self, request, feed_id): 
            feed = self.get_object(feed_id) #self를 통해 클래스 내 인스턴스에 접근
            # feed를 시리얼라이징
            serializer = FeedSerializer(feed)
            # 결과를 HTTP 응답으로 반환
            return Response(serializer.data)

     

    뷰 생성 -> url 맵핑!

    ## feeds/urls.py
    
    from django.urls import path
    from . import views
    
    urlpatterns = [
        # 전체게시글 조회이기때문에 루트경로로 입력
        # .as_view()는 APIView와 url을 맵핑시켜주는 함수
        path("",views.Feeds.as_view()),
        path("<int:feed_id>",views.FeedDetail.as_View())
    ]

     

     

    새로운 유저 생성
    게시글 작성
    게시글 목록 확인
    사용자 추가 확인

     

     

    "user": 가 보여주는 정보를 depth= 로 조정할 수 있다.

    depth=0일 경우에 모든 정보를 보여주므로 users/serializers.py를 생성해 관리해준다.

    ## users.serializers.py
    
    from rest_framework.serializers import ModelSerializer
    from .models import User
    
    class FeedUserSerializer(ModelSerializer):
        class Meta:
            model = User
            fields = ("username","email",)

     

    feeds/serializers.py에 연결한다

    ## feeds/serializers.py
    
    from rest_framework.serializers import ModelSerializer
    from .models import Feed
    from users.serializers import FeedUserSerializer
    
    
    # ModelSerializer는 직렬화를 해주는 역할
    class FeedSerializer(ModelSerializer):
        # 직렬할 것에 대한 특징
        
        # 여기서 user는 FeedUserSerializer에서 해석해줘
        user = FeedUserSerializer()
        
        class Meta:
            model = Feed
            fields = "__all__"
            depth = 1

     

    user의 username, email만 보여준다

     

    (3) POST /feeds 피드 데이터 생성하기 (포스팅 글 작성)

     

    새로운 피드 데이터를 생성하는 것은 전체 피드가 보여지는 url에서 실행되어야 한다.(?)

    그래서 Feeds 클래스에서 post 함수 생성

    ## feeds/views.py
    
    from django.shortcuts import render
    # RestFramework에서 제공하는 APIView를 사용
    from rest_framework.views import APIView
    from .models import Feed
    # 만든 시리얼라이즈 import
    from .serializers import FeedSerializer
    from rest_framework.response import Response
    # NotFound import
    from rest_framework.exceptions import NotFound
    
    
    # Create your views here
    class Feeds(APIView):
        # 전체 게시글 조회
        def get(self, request):
            # 전체 객체데이터를 가짐
            feeds = Feed.objects.all()
            
            # 객체 -> JSON (시리얼라이즈)
            # ModelSerializer가 상속된 FeedSerializer 클래스가 다수의 feeds객체를 직렬화 할것이라는 의미
            serializer = FeedSerializer(feeds, many=True)
            # 직렬화된 데이터를 반환
            return Response(serializer.data)
        
        # 유저가 보낸 request를 다룸
        def post(self, request):
            # 역직렬화 (클라이언트가 보내준 json -> object 형태로 변경)
            serializer = FeedSerializer(data=request.data)
            
            # 시리얼라이저 클래스에서 정의된 유효성 검사 규칙에 데이터가 준수하면 True 반환
            if serializer.is_valid():
                feed = serializer.save(user=request.user)
                serializer = FeedSerializer(feed)
                return Response(serializer.data)
            else :
                return Response(serializer.errors)
            
            
    class FeedDetail(APIView):
        def get_object(self, feed_id):
            try:
                return Feed.objects.get(id=feed_id)
            # feed가 존재하지 않을 경우
            except Feed.DoesNotExist :
                raise NotFound
        
        def get(self, request, feed_id): 
            feed = self.get_object(feed_id) #self를 통해 클래스 내 인스턴스에 접근
            # feed를 시리얼라이징
            serializer = FeedSerializer(feed)
            # 결과를 HTTP 응답으로 반환
            return Response(serializer.data)

    Post 기능이 추가됨

     


    이렇게 포스트하려고 하면
    요런 이슈 발생 근데 강의에서는 오류가 없다. 왜?

     

    username이 있다고 해서 다른 이름으로 변경해서 입력해도 데이터는 로그인된 username으로 들어간다

    왜 오류가 나는지? 

     


     

     

    더보기

    "detail": "JSON parse error - Extra data: line 9 column 1 (char 181)"

    발생해서 한 줄로 json 데이터 입력했더니 잘 들어갔다

    username을 변경해 post했지만 들어간 데이터의 username은 로그인된 이름이다.


    Users API

     

    (1) api/v1/users 에서의 POST 기능 만들기

     

    시리얼라이저 생성

    ## users.serializers.py
    
    from rest_framework.serializers import ModelSerializer
    from .models import User
    
    class FeedUserSerializer(ModelSerializer):
        class Meta:
            model = User
            fields = ("username","email",)
            
            
    ## User API에 필요할 시리얼라이저 생성
    class MyInfoUserSerializer(ModelSerializer):
        class Meta:
            model = User
            fields = "__all__"

     

    더보기

    >>> MyInfoUserSerializer는 ModelSerializer을 상속한다.

    Django REST Framework에서 ModelSerializer는 Django 모델과 해당 모델의 필드를 기반으로 자동으로 시리얼라이저를 생성한다. 따라서 MyInfoUserSerializer(request.data)를 사용하면 HTTP 요청에서 받은 데이터(request.data)를 해당 시리얼라이저로 변환할 수 있다.

     

    >>> ModelSerializer는 또 Serializer를 상속한다.

    ModelSerializerSerializer의 모든 기능을 포함하면서도 Django 모델과의 상호작용을 간편하게 만들어준다.

    Serializer 클래스는 Django REST Framework의 시리얼라이저 중 가장 기본이 되는 클래스로, 모델과 관련된 정보 없이도 데이터를 직렬화하고 역직렬화할 수 있다. 반면에 ModelSerializer는 특정 Django 모델과 연결되어 해당 모델의 필드 구조를 기반으로 시리얼라이저를 자동으로 생성한다. 따라서 ModelSerializer는 Serializer를 상속하여 모델과의 상호작용을 간편하게 만들어주는 특수한 유형의 시리얼라이저입니다.

     

    더보기

    일반적으로 APIViewResponse는 함께 사용됩니다. APIView는 Django REST Framework에서 뷰(View)를 만들기 위한 기본 클래스이며, Response는 클라이언트로부터의 요청에 대한 응답을 생성하기 위한 클래스입니다. APIView를 사용하면 HTTP 요청을 처리하고, 필요한 데이터를 추출하여 Response 객체를 생성하여 클라이언트에게 반환할 수 있습니다.

    ## users/veiws.py
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.exceptions import NotFound, ParseError
    from .models import User
    from .serializers import MyInfoUserSerializer
    from django.contrib.auth.password_validation import validate_password
    
    # Create your views here.
    class Users(APIView):
        def post(self, request):
            # password(검증->해쉬화->저장)
            password = request.data.get('password')
            serializer = MyInfoUserSerializer(data=request.data)
            
            try:
                validate_password(password)
            except:
                raise ParseError("Invalid password")
            
            if serializer.is_valid():
                user = serializer.save()
                user.set_password(password)
                user.save()
                
                serializer = MyInfoUserSerializer(user)
                return Response(serializer.data)
            
            else:
                raise ParseError(serializer.errors)
            # the other

     

    urls.py 파일 생성

    # users/urls.py
    
    from django.urls import path
    from . import views
    
    urlpatterns = [
        path("", views.Users.as_view())
    ]

     

    config/urls.py 에 연결

    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path("admin/", admin.site.urls),
        path("api/v1/feeds/", include("feeds.urls")), #feeds 앱의 API 버전1
        path("api/v1/users/", include("users.urls"))
    ]

     

     

    http://127.0.0.1:8000/api/v1/users
    데이터 정상 생성

     

     

    (2) 사용자 데이터를 읽어오는 GET, PUT 추가

    주소는 /users/myinfo

    사용자의 정보 보기, 사용자 정보 업데이트하기

     

    ## users/veiws.py
    
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.exceptions import NotFound, ParseError
    from .models import User
    from .serializers import MyInfoUserSerializer
    from django.contrib.auth.password_validation import validate_password
    
    #[POST] 유저 생성 API
    # api/v1/users 
    class Users(APIView):
        def post(self, request):
            # password(검증->해쉬화->저장)
            password = request.data.get('password')
            serializer = MyInfoUserSerializer(data=request.data)
            
            try:
                validate_password(password)
            except:
                raise ParseError("Invalid password")
            
            if serializer.is_valid():
                user = serializer.save()
                user.set_password(password)
                user.save()
                
                serializer = MyInfoUserSerializer(user)
                return Response(serializer.data)
            
            else:
                raise ParseError(serializer.errors)
            # the other
            
    
    #[GET, PUT] 데이터 업데이트
    # api/v1/users/myinfo
    class MyInfo(APIView):
        def get(self, request):
            user = request.user
            serializer = MyInfoUserSerializer(user)
            
            return Response(serializer.data)
        
        def put(self, request):
            user = request.user
            serializer = MyInfoUserSerializer(user,
                                              data=request.data, # 업데이트하려는 데이터
                                              partial=True) # 모델의 인스턴스가 일부만 있는 경우에도 허용
            
            if serializer.is_valid():
                user = serializer.save()
                serializer = MyInfoUserSerializer(user)
                return Response(serializer.data)
            
            else:
                return Response(serializer.errors)
    # users/urls.py
    
    from django.urls import path
    from . import views
    
    urlpatterns = [
        path("", views.Users.as_view()), # 주소: api/v1/users
        path("myinfo", views.MyInfo.as_view()) # 주소: api/v1/users/myinfo
    ]

     

    GET 

    http://127.0.0.1:8000/api/v1/users/myinfo

     

    PUT

    http://127.0.0.1:8000/api/v1/users/myinfo
    데이터 업데이트
    업데이트 확인

     


    Reviews API

    (1) /reviews 로 전체 리뷰 데이터 조회

    (2) /reviews/review_id 로 특정 id 리뷰데이터 조회

     

    * reverse_accessor 사용

     

    모델 만들고

    ## reviews/models.py
    
    from django.db import models
    from common.models import CommonModel
    
    class Review(CommonModel):
        content = models.CharField(max_length=120)
        likes_num = models.PositiveIntegerField(default=0)
        
        user = models.ForeignKey("users.User", on_delete=models.CASCADE)
        feed = models.ForeignKey("feeds.Feed", on_delete=models.CASCADE)

     

    장고 세팅에 알려주고

    ## config/settings.py
    
    
    CUSTOM_USER_APPS = [
        "users.apps.UsersConfig",
        "boards.apps.BoardsConfig",
        "common.apps.CommonConfig",
        "feeds.apps.FeedsConfig",
        "reviews.apps.ReviewsConfig",
        "rest_framework"
    ]

     

    마이그레이트

    # 파일 저장 꼬옥
    
    python manage.py makemigrations
    python manage.py migrate

     

    뷰 등록

    ## reviews.views.py
    
    from django.shortcuts import render
    from .models import Review
    from .serializers import ReviewSerializer
    from rest_framework.response import Response
    from rest_framework.views import APIView
    from rest_framework.exceptions import NotFound, NotAuthenticated
    
    # 전체 댓글 조회
    class Reviews(APIView):
        def get(self, request):
            reviews = Review.objects.all()
            
            # 시리얼라이즈
            serializer = ReviewSerializer(reviews, many=True) # 리뷰가 여러개
            return Response(serializer.data)
    
    # 특정 아이디 댓글만 조회
    class ReviewsDetail(APIView):
        def get(self, request, review_id):
            try:
                review = Review.objects.get(id=review_id)
            except Review.DoesNotExist:
                raise NotFound
            
            serializer = ReviewSerializer(review)
            return Response(serializer.data)

     

    이 과정에서 시리얼라이즈 생성

    ## reviews/serializers.py
    
    from rest_framework.serializers import ModelSerializer
    from .models import Review
    
    class ReviewSerializer(ModelSerializer):
        class Meta:
            model = Review
            fields = "__all__"

     

    뷰 작성 끝나면 reviews/urls.py 파일 생성 후

    ## reviews/urls.py
    
    from django.urls import path
    from . import views
    
    urlpatterns = [
        path("", views.Reviews.as_view()),
        path("<int:review_id>", views.ReviewsDetail.as_view()),
    ]

     

    config/urls.py 에 맵핑

    ## config/urls.py
    
    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path("admin/", admin.site.urls),
        path("api/v1/feeds/", include("feeds.urls")), #feeds 앱의 API 버전1
        path("api/v1/users/", include("users.urls")),
        path("api/v1/reviews/", include("reviews.urls"))
    ]

     

    admin을 등록해야 관리자페이지에서 관리 가능

    ## reviews/admin.py
    
    from django.contrib import admin
    from .models import Review
    
    @admin.register(Review)
    class ReviewAdmin(admin.ModelAdmin):
        pass

     

    댓글을 몇 개 작성해주었다.
    http://127.0.0.1:8000/api/v1/reviews/
    특정 유저의 댓글만 확인할 수 있다


    /feeds/ 에 Review도 보이게 연결

    ## feeds/serializers.py
    
    from rest_framework.serializers import ModelSerializer
    from .models import Feed
    from users.serializers import FeedUserSerializer
    from reviews.serializers import ReviewSerializer
    
    # ModelSerializer는 직렬화를 해주는 역할
    class FeedSerializer(ModelSerializer):
        # 직렬할 것에 대한 특징
        
        # 여기서 user는 FeedUserSerializer에서 해석해줘
        user = FeedUserSerializer(read_only=True)
        
        # 댓글은 게시글이 작성될 때 변경되면 안되고
        # 한 개의 게시글에 여러개의 댓글이 들어갈 수 있다.
        review_set = ReviewSerializer(read_only=True, many=True)
        
        class Meta:
            model = Feed
            fields = "__all__"
            depth = 1

    review_set 등장

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

    EmailField & CharField Difference  (0) 2024.03.18
    ORM  (0) 2024.03.08
    Django 2일차 - Admin Panel  (0) 2024.03.07
    Django 2일차  (1) 2024.03.07
    URL, VIEW 개념  (0) 2024.03.06
Designed by Tistory.