[TIL 14일자] 데브코스 데이터엔지니어링

·2023년 4월 27일
0

데브코스

목록 보기
13/55
post-thumbnail
post-custom-banner

📚 오늘 공부한 내용

1. User 추가

  • setting.pyINSTALLED APPS를 보면 django.contrib.admindjango.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;

2. User 관리하기

  • 먼저 serializer.pyUserSerializer을 추가해 준다.
  • PrimaryKeyRelatedField는 class인 User의 PrimaryKey를 통해서 여러 개의 question을 가지고 있다고 명시한 것이다.
  • fields에 추가하지 않고 따로 선언한 이유는 User 테이블에 있는 것이 아니라 Question 테이블에 있는 user_id라는 필드가 있고, 그걸 통해서 UserQuestion을 불러올 수 있는 것이기 때문이다.
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를 통해 화면에 보여 줄 UserListUserDetail 창을 만든다.
  • 이때는 Question과 유사하게 ListAPIViewRetrieveAPIView를 사용하도록 한다.
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()),
]

3. User 생성하기

3-1. Django에서 제공하는 기능을 이용한 User 생성 화면 만들기

1) 사용할 모듈 import

  • genericrest_framework에서 제공하는 generics와 비슷한 역할을 한다.
  • UserCreationFormForm을 일일이 작업하지 않아도 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()),
]
  • 다음과 같은 회원가입 화면이 나오며 만약 있는 계정으로 회원 가입을 넣을 시 그 부분도 안내해 준다.

3-2. Django rest_framework에서 제공하는 기능을 이용한 User 생성 화면 만들기

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에 구현

  • 회원 가입 시 id와 password를 유효성 검사 후 테이블에 저장해 주어야 하므로 create 기능이 필요하다.
  • 또한 비밀번호를 두 번 입력하여 만약 두 비밀번호가 일치하지 않을 경우에 대한 처리도 해 주어야 한다. 이때 django.contrib.auth.password_validationvalidate_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}}

4. User 권한 관리

  • 로그인 한 사용자만 데이터를 생성할 수 있도록 권한 부여
  • 로그인 한 사용자 중 해당 질문을 작성한 사용자만 질문을 수정 및 삭제할 수 있도록 권한 부여

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.urlsimport 하여 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에서 QuestionSerializerowner 추가

  • owner는 수정되면 안 되므로 readonly로 설정해 준다.
class QuestionSerializer(serializers.ModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')

    class Meta:
        model = Question
        fields = ['id', 'question_text', 'pub_date', 'owner']
  • 이때 owner가 로그인 한 사용자로 들어가게 기능을 구현해 주어야 한다. 이는 views.pyQuestionList에 생성과 관련된 함수를 추가하도록 하자.

3) views.pyQuestionList 기능 구현

  • 데이터를 생성할 때 owner를 입력받지 않아도 로그인한 사용자의 정보가 owner로 들어가도록 기능을 구현한다.
  • 또한 로그인하지 않은 사용자라면 readonly만 가능하도록 구현해 준다.
  • 하지만 우리는 추가적으로 로그인한 사용자의 정보와 owner의 정보가 다르면 상세 정보를 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_frameworkpermissions를 사용해 준다.
  • 로그인한 사용자가 작성한 owner와 같은지 비교 후 같다면 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

✔ [특강] Git/Github 익히기

Git이란?

  • 분산 환경을 지원하는 소스 버전 컨트롤 시스템 (분산 개발)
  • SVN/CVS(중앙 개발)에 비해 헌저하게 빠르지만 사용법은 훨씬 복잡하다.
  • 장점
    • 다수의 개발자가 공동 개발 가능
    • 코드 리뷰 가능
    • 히스토리를 저장하기 때문에 코드 백업이 가능
    • 과거 시점으로 롤백도 가능 및 버전 간의 이동도 가능하다.
  • 기능: clon, init, add, commit, push, pull, merge, branch, checkout, rebase, cherry-pick.reset, revert, git-flow

알아야 하는 Git 용어

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

좋은 PR 포맷

  • 주요 변경 사항: 간단한 설명
  • 링크: 디자인 시안 링크/슬랙 관련 대화 링크/ Trello나 JIRA 링크 공유
  • 시급한 정도 (데드라인): 보통, 긴급, 천천히
  • 중점적으로 봐주었으면 하는 경우: 변경 사항이 큰 경우 집중해야 할 부분

🔎 어려웠던 내용 & 새로 알게 된 내용

1. reversereverse_lazy

  • 먼저 Django에서는 url을 편하게 사용할 수 있는 기능을 제공하는데 reversereverse_lazy는 둘 다 resolve(url에 매핑되는 view에 대한 정보를 얻어올 때)하는 함수이다.
  • reverseTemplate의 url 태그와 비슷한 역할을 한다.
reverse(view_name, urlconf=None, args=None, kwargs=None, current_app=None)
  • reverse_lazyreverse를 사용한 url 반환이 urlconf 설정 값이 로딩되기 전에 필요해지는 경우 사용된다.
  • 즉, reverse가 동작하기 위해서는 Django Project에 대한 초기화 작업이 모두 완료되고 나서야 가능해지는데 클래스 내부에서 reverse 호출하면 해당 소스 파일이 import가 되면서 클래스 정의가 이루어질 때 호출이 된다. 이때 reverse를 호출하면 오류가 발생하므로 reverse_lazy를 사용해야 한다. reverse를 수행하는 시점이 실제로 reverse 값을 참조하는 시점으로 지연되어 수행되기 때문이다.
  • 정리하면 class 내부에서 변수url 반환 값을 선언하고자 할 때는 reverse_lazy, class의 함수 내부나 함수 내부에서 url 값을 선언하고자 할 때는 reverse를 사용한다.

✍ 회고

git/github는 현업에서 가장 많이 쓰이는 형상 관리 툴이다. 여태까지 업무를 하면서는 svn과 TFS를 주로 사용해서 써 볼 일이 없었는데 취업을 준비하면서 앞으로의 프로젝트들, 그리고 과제들에서 많이 써 봐야 되겠다고 생각하였다. git과 관련해서는 배워야 할 것도 많고 공부해야 할 것도 많은 것 같다.

profile
송의 개발 LOG
post-custom-banner

0개의 댓글