일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- dag 작성
- Serializer
- ETL
- docker
- docker hub
- 데이터레이크
- selenium
- SQL
- 컨테이너 삭제
- 웹 스크래핑
- 알고리즘
- 데이터마트
- Kafka
- airflow
- Hive
- 데이터 웨어하우스
- Django
- redshift
- dag
- truncate
- spark
- snowflake
- 웹 크롤링
- 데이터파이프라인
- AWS
- Django Rest Framework(DRF)
- docker-compose
- ELT
- yarn
- airflow.cfg
- Today
- Total
개발 기록장
04. Python Django 프레임웍을 사용해서 API 서버 만들기(4) 본문
학습 주제: Django Rest Framework(DRF), User 생성 및 관리, 상속(Inheritance)와 오버라이딩(Overriding), POSTMAN
User
: 회원 객체
- User 추가
- 회원 객체 추가
- polls/models.py
- owner 변수에 유저 객체 생성
- models.ForeignKey(...on_delete=models.CASCADE, ...): owner가 삭제되면 Question도 삭제(외래키 관계)
class Question(models.Model):
question_text = models.CharField(max_length = 200, verbose_name = "질문")
pub_data = models.DateTimeField(auto_now_add= True, verbose_name = "생성일")
#추가
owner = models.ForeignKey('auth.User', related_name='questions', on_delete=models.CASCADE, null =True)
@admin.display(boolean = True, description = "최근생성(하루기준)")
def was_published_recently(self):
return self.pub_data >= timezone.now()- datetime.timedelta(days=1)
def __str__(self):
if self.was_published_recently():
new_badge = 'NEW!!!'
else:
new_badge = ''
return f'{new_badge}제목:{self.question_text}, 날짜: {self.pub_data}'
- User 관리
- 회원 기능 어디에/어떻게 사용할 것인가 정의
- polls_api/serializers.py
- User객체를 Serialize 해주는 Serializer 생성
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
questions = serializers.PrimaryKeyRelatedField(many = True, queryset= Question.objects.all())
class Meta:
model = User
fields = ['id', 'username', 'questions']
- polls_api/urls.py
- 회원 리스트: ex)http://127.0.0.1:8000/rest/users/
- 회원 상세(detail) 페이지: ex)http://127.0.0.1:8000/rest/users/1
from django.urls import path, include
from .views import *
urlpatterns = [
path('question/', QuestionList.as_view(), name='question-list'),
path('question/<int:pk>/', QuestionDetail.as_view(), name='question-detail'),
path('users/',UserList.as_view(), name = 'user-list'), #추가
path('users/<int:pk>/',UserDetail.as_view(), name = 'signup'), #추가
]
User 생성
: 회원 가입
- Form을 통한 User 생성
- django.contrib.auth에서 지원하는 UserCreation폼을 사용하여 회원가입
- polls/views.py
- reverse_lazy: SignupView 내부 작업을 끝내고, 생성된 값을 url로 반환하기 위해 사용(reverse를 lazy하게)
- template_name: 해당 html에 폼 작성
from django.views import generic
from django.urls import reverse_lazy
from django.contrib.auth.forms import UserCreationForm
#회원가입 폼
class SignupView(generic.CreateView):
form_class = UserCreationForm
success_url = reverse_lazy('user-list') #회원가입 성공 시 '/rest/users'로 이동
template_name ='registration/signup.html'
- polls/templates/regitstration/signup.html
- {{form.as_p}}: UserCreationForm 작성
<h2>회원가입</h2>
<form method="post">
{% csrf_token %}
{{form.as_p}}
<button type="submit">가입하기</button>
</form>
- polls/urls.py
- http://127.0.0.1:8000/polls/signup/
from django.urls import path
from . import views
from .views import *
app_name = 'polls'
urlpatterns = [
path('', views.index, name='index'),
path('<int:question_id>/',views.detail, name = 'detail'),
path('<int:question_id>/result/',views.result, name = 'result'),
path('<int:question_id>/vote/', views.vote, name = 'vote'),
path('signup/',SignupView.as_view()), #추가
]
- Serializer을 통한 User 생성
- Rest API로 회원가입
- polls_api/serializers.py
- 패스워드 재확인하고 회원가입
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
password2 = serializers.CharField(write_only=True, required=True)
#패스워드 검증(일치/불일치 확인)
def validate(self, attrs):
if attrs['password'] != attrs['password2']:
raise serializers.ValidationError({"password": "두 패스워드가 일치하지 않습니다."})
return attrs
def create(self, validated_data):
user = User.objects.create(username=validated_data['username'])
user.set_password(validated_data['password'])
user.save()
return user
class Meta:
model = User
fields = ['username', 'password','password2']
- polls_api/views.py
- RegisterSerializer로부터 User 정보 받아와서 generics의 CreateAPIView 상속받아 User 등록
from polls_api.serializers import RegisterSerializer
class RegisterUser(generics.CreateAPIView):
serializer_class = RegisterSerializer
- polls_api/urls.py
- http://127.0.0.1:8000/rest/register/
from django.urls import path, include
from .views import *
urlpatterns = [
path('question/', QuestionList.as_view(), name='question-list'),
path('question/<int:pk>/', QuestionDetail.as_view(), name='question-detail'),
path('users/',UserList.as_view(), name = 'user-list'),
path('users/<int:pk>/',UserDetail.as_view(), name = 'signup'),
path('register/',RegisterUser.as_view()), #추가
]
User 권한 관리
: 로그인 된 상태 : Create, Read, Update, Delete 모두 가능
: 로그인 안 된 상태 : Read Only
- permissions
- 권한 생성
- polls_api/urls.py
- 권한 인증을 위한 url
from django.urls import path, include
from .views import *
urlpatterns = [
path('question/', QuestionList.as_view(), name='question-list'),
path('question/<int:pk>/', QuestionDetail.as_view(), name='question-detail'),
path('users/',UserList.as_view(), name = 'user-list'),
path('users/<int:pk>/',UserDetail.as_view(), name = 'signup'),
path('register/',RegisterUser.as_view()),
path('api-auth/', include('rest_framework.urls')), #추가
]
- mysite/settings.py
- 로그인 성공: 'question-list'로 리다이렉트
- 로그인 실패: 'question-list'로 리다이렉트
from django.urls import reverse_lazy
LOGIN_REDIRECT_URL = reverse_lazy('question-list')
LOGOUT_REDIRECT_URL = reverse_lazy('question-list')
- polls_api/serializers.py
- 질문 작성자가 아니라면 질문 읽기만 가능함.
class QuestionSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username') #질문 작성자X-> ReadOnly
class Meta:
model = Question
fields = ['id', 'question_text', 'pub_data', 'owner']
- polls_api/permissions.py
- IsOwnerOrReadOnly: 자기가 작성한 글만 수정할 수 있도록 하는 권한 생성
from rest_framework import permissions
#로그인된 사용자, 즉 자기가 작성한 글만 수정할 수 있도록(보기는 아무나 가능)
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request,view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.owner ==request.user
- polls_api/views.py
- IsAuthenticatedOrReadOnly: 인증된 사용자만 질문 생성 가능 나머지는 읽기만 가능
- IsOwnerOrReadOnly(;permmisions.py로부터 생성된 권한): 인증된 사용자만 질문 수정 가능 나머지는 읽기만 가능
- perform_create(): 질문 생성할 때 로그인(인증)된 사용자를 owner로 지정
from polls.models import Question
from polls_api.serializers import *
from rest_framework import generics, permissions
from django.contrib.auth.models import User
from .permissions import IsOwnerOrReadOnly
class QuestionList(generics.ListCreateAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
#로그인된 사용자만 질문 생성 가능
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
#create할 때 이미 로그인된 사용지를 owner로 지정
#오버라이드 됨
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
#로그인된 사용자만 질문 수정가능
permission_classes = [permissions.IsAuthenticatedOrReadOnly,IsOwnerOrReadOnly]
상속(Inheritance)와 오버라이딩(Overriding)
: 파이썬으로 개발을 하다보면, 코드의 재사용성과 생산성 증대를 위해 상속과 오버라이딩의 개념을 많이 이용한다.
- 상속(Inheritance)
- 부모가 자식에게 재산 또는 지위를 물려줌.
- 부모 클래스(하나의 클래스)로부터 자식 클래스(다른 클래스)가 메소드와 속성을 물려받아 그대로 사용하는 것.
- 부모 클래스의 기능을 물려받아 자식 클래스에서 필요한 기능을 추가해 사용
# 부모 클래스: Animal클래스
# 자식 클래스: Dog이라는 클래스
# Animal 클래스 -> Dog 클래스 + walk 메소드
class Animal():
def __init__(self, name):
self.name = name
def speak(self):
return "동물이 울음소리를 냅니다"
class Dog(Animal):
def walk(self):
return "산책을 합니다."
- 오버라이딩(Overriding)
- 부모 클래스(하나의 클래스)로부터 메소드와 속성을 물려받은 자식 클래스(다른 클래스)가 원하는 형태로 내용을 변경하여
사용하는 것.
- 덮어쓰기, 재정의라고도 함.
- 같은 이름의 메서드라도 자식 클래스에서 다른 기능으로 사용될 수 있음.
# 부모 클래스: Animal클래스
# 자식 클래스: Dog이라는 클래스
# Animal 클래스 -> Dog 클래스 + speak 메소드 재정의
class Animal():
def __init__(self, name):
self.name = name
def speak(self):
return "동물이 울음소리를 냅니다"
class Dog(Animal):
def walk(self):
return "산책을 합니다."
#오버라이드
def speak(self):
return "멍멍!"
- super()
- 자식 클래스에서 부모 클래스의 내용을 사용하고 싶은 경우
# 부모 클래스: Animal클래스
# 자식 클래스: Dog이라는 클래스
# Animal 클래스 -> Dog 클래스 + __init__메소드 재정의(super()) + speak 메소드 재정의
class Animal():
def __init__(self, name):
self.name = name
def speak(self):
return "동물이 울음소리를 냅니다"
class Dog(Animal):
#오버라이드 && super()
def __init__(self, name, age):
super().__init__(name)
self.age =age
def walk(self):
return "산책을 합니다."
def speak(self):
return "멍멍!"
POSTMAN
: RESTful API 테스트 플랫폼
: 다양한 HTTP 요청 보내기/응답 결과 확인
: 설치(;POSTMAN 홈페이지)- https://www.postman.com/
Postman API Platform | Sign Up for Free
Postman is an API platform for building and using APIs. Postman simplifies each step of the API lifecycle and streamlines collaboration so you can create better APIs—faster.
www.postman.com
- 로그인 X 상태에서 PUT 요청
- 브라우저에서는 로그인이 안 된 상태의 PUT 요청이 들어왔을때 어떻게 동작하는 지 알 수없다.
- POSTMAN을 이용해 결과를 확인한다.
- PUT 요청
- Headers: Key - Content-Type Value - application/json
- Body: raw - {"question_text":"POSTMAN에서 보는 제목"}
- 결과: 403 에러 - 인증 실패
- 로그인 O 상태에서 PUT 요청
- session 값을 이용해 로그인 인증하고, PUT 요청
- PUT 요청
- Headers: Key - Content-Type Value - application/json
Key - Cookie Value - sessionid = {{개발자 도구에서 세션 id 가져오기}}
Key - X-CSRFToken Value - {{개발자 도구에서 세션 CSRF-Token 가져오기}}
Key - Cookie Value - csrftoken = {{개발자 도구에서 세션 CSRF-Token 가져오기}}
- Body: raw - {"question_text":"POSTMAN에서 보는 제목"}
- 결과: 200 OK - PUT 성공
- GET 요청
- 위에서 Update 성공한 질문 상세페이지(detail) 확인
- 결과: 200 OK - GET 성공
공부하며 느낀점
클래스를 구현하면서 상속의 강력함을 경험했다. 또 이전에는 권한 문제를 함수로만 해결했는데, Permissions을 사용할 수 있다는 것을 알게 되어 눈을 다시 뜬 기분이다. (지금까지가 거짓말 같다...)
'데브코스(DE) > 장고 활용한 API 서버 제작' 카테고리의 다른 글
05. Python Django 프레임웍을 사용해서 API 서버 만들기(5) (0) | 2024.04.12 |
---|---|
03. Python Django 프레임웍을 사용해서 API 서버 만들기(3) (1) | 2024.04.10 |
02. Python Django 프레임웍을 사용해서 API 서버 만들기(2) (0) | 2024.04.09 |
01. Python Django 프레임웍을 사용해서 API 서버 만들기(1) (0) | 2024.04.08 |