이번 학습 내용은 User (사용자)를 추가하는 방법에 대해 살펴보고, 상속 관련한 개념에 대해 알아보고자 한다.
User를 생성하는 방법에는 여러가지가 있다.
이후 User 권한 관리에 대해 알아보고 POSTMAN 학습도 진행할 계획
기존에 만들었던 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
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>
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()),
]
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()),
사용자가 만든 질문 기록하는 기능 추가!
# 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]
개념
하나의 클래스가 다른 클래스로부터 메소드와 속성을 그대로 물려받아 사용할 수 있는 기능을 제공합니다. 이 때 자신의 메소드와 속성을 물려주는 클래스를 부모 클래스(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 "멍멍!"
기존에 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'
RESTful API 테스트를 위한 플랫폼으로 다양한 HTTP Request와 response 결과를 쉽게 확인할 수 있다.
금일 Git 공부부터 학교도 왔다갔다하느라 TIL이 많이 늦었다...
장고 꼭 복습해보자. 안하면 못따라간다🙄반드시 알아두어야 하는 부분
- models.py 수정 시 cmd에서
: makemigrations & migrate 실행해 web에 반영- 새로운 앱 추가 시 project_name/settings.py 에서 처리
: 'app_name.apps.AppNameConfig' 추가