DevCourse TIL Day4 Week4

김태준·2023년 4월 27일
0

Data Enginnering DevCourse

목록 보기
16/93
post-thumbnail

이번 학습 내용은 User (사용자)를 추가하는 방법에 대해 살펴보고, 상속 관련한 개념에 대해 알아보고자 한다.
User를 생성하는 방법에는 여러가지가 있다.

    1. app/models.py
    1. Form 사용해 User 생성
    1. Serializer 사용해 User 생성

이후 User 권한 관리에 대해 알아보고 POSTMAN 학습도 진행할 계획

✅ User 추가하기

🎈 app/models.py

기존에 만들었던 Question 모델에 작성자를 추가하고 작성자만 자신의 질문을 수정하는 기능 만들고 사용자 관리 기법 만들기!

app/models.py

## Qustion 모델에 USER 추가하기
class Question(models.Model):
    qustion_text = models.CharField(max_length=200, verbose_name = '질문')
    pub_date = models.DateTimeField(auto_now_add = True, verbose_name = '생성일')
    # owner 유저가 삭제되면 question도 삭제되도록 on_delete 지정
    owner = models.ForeignKey('auth.User', related_name = 'quesitons', on_delete = models.CASCADE, null = True)

    @admin.display(boolean = True, description = '최근 생성(하루기준)')
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
    def __str__(self):
        return f'제목: {self.question_text}, 날짜: {self.pub_date}'

### < Django Shell >
from django.contrib.auth.models import User
User
# 결과 : <class 'django.contrib.auth.models.User'>

User._meta.get_fields()
User.objects.all()
# 결과 : <QuerySet [<User: admin>]>

from polls.models import *
user = User.objects.first()
user.questions.all()
print(user.questions.all().query)
# query 결과
SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date", "polls_question"."owner_id" FROM "polls_question" WHERE "polls_question"."owner_id" = 1

✅ Form 사용해 User 생성하기

django에서 제공하는 기능으로 생성

# app/views.py

### django로 User 생성하기
from django.views import generic
#### 이름 기반 url 제작토록 하는 reverse_lazy #####
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')
    template_name = 'registration/signup.html'

# app/urls.py
## User 생성하기
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>/vote/', views.vote, name='vote'), 
    path('<int:question_id>/result/', views.result, name='result'), 
    path('signup/', SignupView.as_view(), )
]

< django shell >
from django.urls import reverse_lazy
reverse_lazy('user-list')
# 결과 : '/rest/users/'
<!-- app/registration/signup.html -->
<h2>회원가입</h2>
    <form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">가입하기</button>
</form>

✅ Serializer 사용해 User 생성

django rest_framework에서 제공하는 Serializer로 User만들어보기!

### app_api/serializers.py

### user 생성하기
from django.contrib.auth.models import User
from django.contrib.auth.password_validation import validate_password

class RegisterSerializer(serializers.ModelSerializer):
	# 1234 같은 평벙한 패스워드 배제 (validators)
    password = serializers.CharField(write_only = True, required = True, validators = [validate_password])
    password2 = serializers.CharField(write_only = True, required = True)
    # password 1차 2차 비교
    def validate(self, attrs):
        if attrs['password'] != attrs['password2']:
            raise serializers.ValidationError('비밀번호가 일치하지 않습니다.')
        return attrs
    # user모드에서 별도의 password가 없어서 함수 생성
    def create(self, validated_data):
        user = User.objects.create(username = validated_data['username'])
        user.set_password(validated_data['password'])
        user.save()
        return user
    # user 모델 기반임을 보여줌
    class Meta:
        model = User
        fields = ['username', 'email', 'password']


### app_api/views.py
# User 생성하기
from polls_api.serializers import RegisterSerializer

class RegisterUser(generics.CreateAPIView):
    serializer_class = RegisterSerializer

### app_api/urls.py
# User 생성하기
urlpatterns = [
    path('question/', QuestionList.as_view(), name='question-list'),
    path('question/<int:pk>/', QuestionDetail.as_view()),
    path('users/', UserList.as_view(),name='user-list'),
    path('users/<int:pk>/', UserDetail.as_view()),
 	# 만들어놓은 view에서 생성
 	path('register/', RegisterUser.as_view()),
]

✅ User 관리하기

Serializer 름 만들고 , views를 통해 구현

# app_api/serializers.py

## User 관리하기
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
	# 자신의 ID(primaryKey)를 통해 질문 확인
    qustions = serializers.PrimaryKeyRelatedField(many = True, queryset = Question.objects.all())
    class Meta:
        model = User
        fields = ['id', 'user_name', 'questions']

# app_api/views.py

## User 관리하기
from django.contrib.auth.models import User
from polls_api.serializers import UserSerializer
from rest_framework import generics
class UserList(generics.ListAPIView):
    queryset = User.objects.all()
    serializers_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    
# view만들었으니 url 연결 api/urls.py
path('users/', UserList.as_view(),name='user-list'),
path('users/<int:pk>/', UserDetail.as_view()),

✅ User 권한 관리하기

사용자가 만든 질문 기록하는 기능 추가!

# app_api/urls.py
# 아래 경로 추가하기
path('api-auth/', include('rest_framework.urls')) 

# setting.py 추가 (url setting 해주기)
from django.urls import reverse_lazy
LOGIN_REDIRECT_URL = reverse_lazy('question-list')
LOGOUT_REDIRECT_URL = reverse_lazy('question-list')

api/serializer.py 
class QuestionSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')

    class Meta:
        model = Question
        fields = ['id', 'question_text', 'pub_date', 'owner']

# 자기가 만든 question에 한해 지울 수 있도록 (owner에 한해) permission 파이썬 파일 생성
# 아래 파이썬 파일 추가!
# api/permissions.py 

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


api/views.py
# User 권한 관리
from rest_framework import generics,permissions
from .permissions import IsOwnerOrReadOnly

class QuestionList(generics.ListCreateAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer
    # 로그인된 상태에서만 무언가 Create하도록 함 
    # (아래 코드이후로 로그인을 안하면 django rest_framework에서 제공하는 화면에서 put을 하는 입력창이 보이지 않음)
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    # QustionList에서 Create 할 때 이미 로그인된 사용자를 owner 처리!
    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)
# onwer가 아니면 수정 X
class QuestionDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Question.objects.all()
    serializer_class = QuestionSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]

✅ Inheritence & Overriding

개념
하나의 클래스가 다른 클래스로부터 메소드와 속성을 그대로 물려받아 사용할 수 있는 기능을 제공합니다. 이 때 자신의 메소드와 속성을 물려주는 클래스를 부모 클래스(Parent Class) 또는 상위 클래스(Super class) 라고 지칭합니다. 반대로 그것을 물려받아서 사용하는 클래스는 자식 클래스(Child Class) 또는 하위 클래스(Sub Class) 라고 부릅니다.

이와 같이 상속받은 메서드의 내용을 자식 클래스에서 변경하여 사용하는 것을 오버라이딩(Overriding ) 이라고 합니다. 상속받은 메서드의 기능을 그대로 사용할 때도 있지만, 자식 클래스에서 필요에 맞게 변경해서 사용해야 하는 경우가 많습니다. 이런 경우에 오버라이딩을 사용하면 같은 이름의 메서드를 자식 클래스에서 다른 기능으로 재정의하여 사용할 수 있습니다.

< 예시 코드 구현은 다음과 같다 >

class Animal():
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        return "동물이 울음소리를 냅니다"

class Dog(Animal):
    # 이 아래에서 __init__ 메소드를 오버라이드 하세요.
    # __init__메소드는 self,name,age 3가지 인자를 전달받아야합니다.
    def __init__(self, name, age):
        super().__init__(name)
        self.age = age
    def walk(self):
        return "산책을 합니다."
    
    def speak(self):
        return "멍멍!"

✅ perform_create

기존에 api/views.py에 포함된 QustionList클래스에서 perform_create함수의 동작을 알아보고자 한다. (상속하는 메서드를 override하는 것)

  • 처음 url 요청이 들어오면 QuestionList에 들어오고 as_view에 의해 post요청은 post메서드로 호출! (view에서 이를 살펴보면 부모 클래스인 ListCreateAPIView를 통해 post를 받음을 알 수 있다.)
  • QustionList인자인 ListCreateAPIView의 post작업은 create함수는 ListCreateAPIView의 인자인 CreateModeMixin으로부터 정의된 create함수로부터 작동한다.

위 과정을 도식화 하면 다음과 같다.

QuestionList
- generics.ListCreateView 를 상속받았고, 이는
	- mixins.CreateModeMixin을 상속받았다
QuestionList안에 정의된 메소드
def get(self, request, *args, **kwargs):
	출처 : generics.ListCreateView
def create(self, request, *args, **kwargs):
	출처 : mixins.CreateModeMixin
def perform_create(self, serializer):
	지워짐 : mixins.CreateModeMixin
    동작함 : QuestionList
# < django shell >
from polls_api.serializers import Question
question_serializer = QuestionSerializer(data = {'question_text':'some_text', "owner" : 'someone'})
# 유효성 검사
question_serializer.is_valid() # 결과 : True
# 유효한 데이터 쳐보기
question_serializer.validated_data
# 결과 : owner가 없음. read_only이기에 serializer로 밀어 넣을 수 없음
OrderedDict([('question_text', 'some text')])
# 단, save 하는 경우 아래와 같이 모두 가능
ex)
question = question.serializer.save(id = 100)
question.id
# 결과 : 100

# 
question = question_serializer.save(id=10000)
question.id
# 결과 : 10000

question.question_text
# 결과 : 'some text'

✅ POSTMAN

RESTful API 테스트를 위한 플랫폼으로 다양한 HTTP Request와 response 결과를 쉽게 확인할 수 있다.

🎇 정리

금일 Git 공부부터 학교도 왔다갔다하느라 TIL이 많이 늦었다...
장고 꼭 복습해보자. 안하면 못따라간다🙄

반드시 알아두어야 하는 부분

  • models.py 수정 시 cmd에서
    : makemigrations & migrate 실행해 web에 반영
  • 새로운 앱 추가 시 project_name/settings.py 에서 처리
    : 'app_name.apps.AppNameConfig' 추가
profile
To be a DataScientist

0개의 댓글