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를 사용한다.