setting.py
의 INSTALLED APPS
를 보면 django.contrib.admin
와 django.contrib.auth
가 있는데 이를 통해 admin 기능을 사용할 수 있고, user를 만들 수 있고 그 유저를 사용할 수 있었던 것이다.models.py
에서 질문의 작성자만 삭제 및 수정할 수 있도록 기능을 구현하기 위해 owner
를 추가해 준다.on_delete=models.CASCADE
는 owner가 삭제되면 question도 삭제된다는 뜻이다.null=True
는 null 값을 가질 수 있다는 뜻이다.from django.db import models
from django.utils import timezone
import datetime
from django.contrib import admin
class Question(models.Model):
question_text = models.CharField(max_length = 200, verbose_name ='질문')
pub_date = models.DateTimeField(auto_now_add = True, verbose_name='생성일')
owner = models.ForeignKey('auth.User', related_name='questions', on_delete=models.CASCADE, null=True) #owner이 삭제가 되면 Question도 다 삭제한다라는 뜻
User.objects.first()
를 통해 추출한 user를 questions.all() 해서 조회하는 쿼리문을 확인하면 다음과 같다.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;
serializer.py
에 UserSerializer
을 추가해 준다.PrimaryKeyRelatedField
는 class인 User의 PrimaryKey를 통해서 여러 개의 question을 가지고 있다고 명시한 것이다.fields
에 추가하지 않고 따로 선언한 이유는 User 테이블
에 있는 것이 아니라 Question 테이블
에 있는 user_id
라는 필드가 있고, 그걸 통해서 User
는 Question
을 불러올 수 있는 것이기 때문이다. from rest_framework import serializers
from polls.models import Question
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']
views.py
를 통해 화면에 보여 줄 UserList
와 UserDetail
창을 만든다.Question
과 유사하게 ListAPIView
와 RetrieveAPIView
를 사용하도록 한다.from django.contrib.auth.models import User
from polls_api.serializers import UserSerializer
class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
urls.py
를 통해 연결할 url
설정을 해 주어야 한다.from django.urls import path
from .views import *
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()),
]
1) 사용할 모듈 import
generic
은 rest_framework
에서 제공하는 generics
와 비슷한 역할을 한다.UserCreationForm
는 Form
을 일일이 작업하지 않아도 User 생성을 위한 Form
을 만들어 준다.from django.views import generic
from django.urls import reverse_lazy
from django.contrib.auth.forms import UserCreationForm
2) views.py
수정
views.py
에서 호출된 것을 바탕으로 SignupView
를 만들어 준다. class SignupView(generic.CreateView):
form_class = UserCreationForm
success_url = reverse_lazy('user-list')
template_name = 'registration/signup.html'
3) signup.html
템플릿 생성
views.py
에서 호출해 준 signup.html
템플릿을 만들어 준다. {{ form.as_p }}
은 UserCreationForm
을 렌더링한 결과를 출력하는 코드<h2> 회원 가입 </h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">가입하기</button>
</form>
4) url
연결
urls.py
에서 url
을 연결해 준다.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('some_url', views.some_url),
path('signup/', SignupView.as_view()),
]
1) `serializers.py에 기본 RegisterSerializer을 생성
serializers.py
에 구성한다.writer_only=True
로 쓴다.from rest_framework import serializers
from polls.models import Question
from django.contrib.auth.models import User
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'password']
extra_kwargs = {'password': {'write_only': True}} #password를 읽으면 안 되므로 write_only
2) views.py
에서 RegisterSerializer
을 이용해 class 생성
CreateAPIView
를 사용한다.class RegisterUser(generics.CreateAPIView):
serializer_class = RegisterSerializer
3) url
연결
from django.urls import path
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()),
path('register/', RegisterUser.as_view()),
]
4) 회원 가입을 위해 필요한 기능 serializers.py
에 구현
create
기능이 필요하다.django.contrib.auth.password_validation
의 validate_password
를 사용해 준다from rest_framework import serializers
from polls.models import Question
from django.contrib.auth.models import User
from django.contrib.auth.password_validation import validate_password
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
password2 = serializers.CharField(write_only=True, required=True)
def create(self, validated_data):
user = User.objects.create(username=validated_data['username'])
user.set_password(validated_data['password'])
user.save()
return user
def validate(self, attrs):
if attrs['password'] != attrs['password2']:
raise serializers.ValidationError({'password': "두 패스워드가 일치하지 않습니다."})
return attrs
class Meta:
model = User
fields = ['username', 'password', 'password2']
extra_kwargs = {'password': {'write_only': True}}
1) 로그인 및 로그아웃 창과 연결되도록 setting.py
수정
reverse_lazy
를 통해 로그인 창과 로그아웃 창과 연결되도록 설정from django.urls import reverse_lazy
LOGIN_REDIRECT_URL = reverse_lazy('question-list')
LOGOUT_REDIRECT_URL = reverse_lazy('question-list')
urls.py
에서 rest_framework.urls
를 import
하여 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()),
path('users/', UserList.as_view(),name='user-list'),
path('users/<int:pk>/', UserDetail.as_view()),
path('register/', RegisterUser.as_view()),
path('api-auth/', include('rest_framework.urls'))
]
2) serializers.py
에서 QuestionSerializer
에 owner
추가
owner
는 수정되면 안 되므로 readonly
로 설정해 준다.class QuestionSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = Question
fields = ['id', 'question_text', 'pub_date', 'owner']
views.py
에 QuestionList
에 생성과 관련된 함수를 추가하도록 하자. 3) views.py
의 QuestionList
기능 구현
owner
를 입력받지 않아도 로그인한 사용자의 정보가 owner
로 들어가도록 기능을 구현한다.readonly
만 가능하도록 구현해 준다.readonly
로 구현해 주고 싶다. 이는 자체적인 기능은 없고 따로 구현해야 한다. 그래서 permissions.py
를 생성한 후 IsOwnerOrReadOnly
클래스를 구현하자.from rest_framework import generics,permissions
from .permissions import IsOwnerOrReadOnly
class QuestionList(generics.ListCreateAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
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]
4) permissions.py
생성
rest_framework
의 permissions
를 사용해 준다.True
를 Return 해 준다.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.use
분산 환경
을 지원하는 소스 버전 컨트롤 시스템 (분산 개발
) Repo
: Repository의 줄임말로 git으로 관리되는 소프트웨어 지칭
Master/Main
: 한 Repo에서 기본이 되는 메인 코드
Branch
: 새로운 기능 개발을 위해 Master나 메인 Branch를 카피해서 새로운 Branch를 만든 작업본. 나중에 원본 Branch와 병합하려는 목적으로 만들어진다.
Clone
: 다른 계정에 존재하는 repo로부터 새로운 local repository를 만드는 것
Commit(Check-in)
: 내가 만든 코드의 변경 사항을 Local Repository에 반영한다.
Local Repository -> Remote Repository (서버) : PUSH (내가 작업 중인 변경 사항들을 서버로 복사하는 것)
Local Repository <- Remote Repository (서버) : PULL (Master와 씽크하는 것)
Merge : 두 Branch 간의 충돌을 해결하는 과정. 많은 경우 자동으로 해결되지만 몇몇 경우는 손으로 직접 해결해야 한다.
git clone repo_url
git remote set-url origin my_repo_url
git checkout 브랜치이름
git commit - m "메시지" 파일이름
git push -u origin 브랜치이름
git request-pull Pull Request
- 작업은 명령어로 하지만
Pull Request
는깃헙 페이지
에서 하는 것을 추천한다.Code Review
시 바꿔야 할 게 명확하면Request Changes
그게 아니라 코드 리뷰라면Commit
1.
reverse
와reverse_lazy
- 먼저 Django에서는
url
을 편하게 사용할 수 있는 기능을 제공하는데reverse
와reverse_lazy
는 둘 다 resolve(url에 매핑되는 view에 대한 정보를 얻어올 때)하는 함수이다.reverse
는Template의 url 태그
와 비슷한 역할을 한다.reverse(view_name, urlconf=None, args=None, kwargs=None, current_app=None)
reverse_lazy
는reverse
를 사용한 url 반환이 urlconf 설정 값이 로딩되기 전에 필요해지는 경우 사용된다.- 즉,
reverse
가 동작하기 위해서는 Django Project에 대한 초기화 작업이 모두 완료되고 나서야 가능해지는데클래스 내부
에서reverse
호출하면 해당 소스 파일이import
가 되면서 클래스 정의가 이루어질 때 호출이 된다. 이때reverse
를 호출하면 오류가 발생하므로reverse_lazy
를 사용해야 한다.reverse
를 수행하는 시점이 실제로reverse
값을 참조하는 시점으로 지연되어 수행되기 때문이다.- 정리하면
class 내부에서 변수
로url 반환 값
을 선언하고자 할 때는reverse_lazy
,class의 함수 내부나 함수 내부
에서url 값
을 선언하고자 할 때는reverse
를 사용한다.