django 1부 끝난 기념으로 정리

yoodeit·2025년 8월 25일
1

일단 만드는 어무해

목록 보기
21/21

urls.py

URL 경로와 실행할 뷰를 매핑하는 역할을 한다.
기본으로 만들어지는 바깥쪽 urls.py와 각 app의 urls.py부터 연결한다.
include를 통해 연결이 가능하다.

기본 urls.py

from articleapp.views import ArticleListView
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('',ArticleListView.as_view(),name='home'),
    path('admin/', admin.site.urls),
    path('accounts/', include('accountapp.urls')),
    path('profiles/', include('profileapp.urls')),
    path('articles/', include('articleapp.urls')),
    path('comments/', include('commentapp.urls')),
    path('projects/', include('projectapp.urls')),
    path('subscribe/', include('subscribeapp.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

url주소에서 accounts/ 뒤쪽은 accountapp.urls에 위임한다는 의미.

각 앱에서의 urls.py

그럼 각 앱에서의 urls.py를 살펴보자.

from accountapp.views import (accountCreateView, accountDeleteView,
                              accountDetailView, accountUpdateView)
from django.contrib.auth.views import LoginView, LogoutView
from django.urls import include, path

app_name="accountapp"

urlpatterns = [
    
    path('login/', LoginView.as_view(template_name = 'accountapp/login.html'), name='login'),
    path('logout/', LogoutView.as_view(next_page="accountapp:helloworld"), name='logout'),
    path('create/', accountCreateView.as_view(),name='create'),
    path('detail/<int:pk>', accountDetailView.as_view(),name='detail'),
    path('update/<int:pk>', accountUpdateView.as_view(),name='update'),
    path('delete/<int:pk>', accountDeleteView.as_view(),name='delete'),
]

accounts/ 뒤쪽의 url을 추가로 달아주고, url별로 각 뷰를 연결한다.

<int:pk>

/<int:pk> 는 URL 경로 일부를 캡쳐해서 뷰에 인자로 넘기는 문법이다. (어떤 형태로 매칭/변환할지) : (뷰에 전달될 키워드 인자 이름) 으로 작성한다.

.as_view()

각 기능에 맞는 view를 임포트해와서 .as_view() 로 띄운다.
as_view() 함수는 클래스형 뷰(CBV)를 URL에 연결할 수 있는 호출 가능한 함수로 바꿔주는 공장 메서드이다. 그래서 함수형 뷰에서는 호출할 필요가 없으나, 클래스형 뷰에서는 호출해주어야 한다. 왜 클래스를 함수로 바꾸어야 하냐면, URLconf에서는 함수가 필요하기 때문에 CBV를 함수로 매핑해주어야만 한다.
브라우저가 특정 URL로 요청을 보냈을 때, Django는 URLconf를 훑어서 매칭된 뷰 콜러블(callable)을 찾는다. 이 때 FBV라면 바로 함수 자체를 반환하면 되겠지만, 클래스일 때는 .as_view()가 반환한 함수를 호출하게 된다.
as_view() 함수에는 인자를 넣어줄 수 있다. 강의에서 배운 인자들의 종류는 아래와 같다.

1) template_name
렌더링할 템플릿의 경로. 원래는 views.py에 정의되어 있는데 urls.py에서 인자로 넘기기도 한다. views.py에서 template_name을 정의하든, urls.py에서 .as_view()의 인자로 template_name을 넘겨주든 둘 다 뷰 인스턴스의 속성을 설정한다고 보면 된다.
다만, urls.py쪽에서 인자로 넘겨준 값이 클래스보다 우선한다는 점을 알아두면 좋다.

2) next_page
로그인/로그아웃 같은 CBV가 어디로 다이렉트할지 결정할 때 쓰는 옵션이다. 뷰 클래스 속성으로 지정할 수도 있지만, 위의 코드에서는 장고에서 기본 제공하는 뷰를 그대로 쓰다보니 인자로 next_page를 알려주어야 하는 경우라고 보면 된다. 만약 클래스에서 속성으로 지정한다면 resolve_url()로 처리가 가능하다.

name 지정
마지막으로 name을 지정해준다. 이러면 app_name과 함께 'accountapp:detail' 이런식으로 활용이 가능하다.

views.py

request를 받아서 Response를 만드는 곳.
Django에서는 URL -> View -> (Model/Form/Template) -> Response로 흐름이 이어진다고 보면 된다.

실제로 기능을 함수, 혹은 클래스로 명시하면 된다.
CBV를 구현한 이번 강의에서는 다양한 클래스를 만들었다.
이미 장고에 있는 뷰를 상속해서 기능을 커스텀한다.

accountapp/views.py

예를 들어 accountapp/views.py에서 createview를 보자.

accountCreateView

class accountCreateView(CreateView):
    model = User
    form_class = UserCreationForm
    success_url = reverse_lazy('accountapp:helloworld')
    # 함수와 클래스가 파이썬에서 불러와지는 방식의 차이 때문에 클래스에서는 reverse_lazy를 써야 함. 기능은 똑같음
    template_name = 'accountapp/create.html'

model
model 자체는 데이터베이스 테이블을 코드로 표현한 클래스.
모델 인스턴스는 그 테이블의 한 행. 한 줄. 객체를 의미한다.

model은 이 뷰가 어떤 모델 인스턴스를 만들 것인지를 지정한다.
다시말해 어떤 테이블에 한 줄을 추가할 것인지를 지정한다고 보면 된다.
CreateView를 상속해서 만든 클래스인데, CreateView자체가 내부에서 ModelForm을 쓰기 때문에, model을 알아야 디비에 form.save()로 객체를 만들 수 있다.

form_class
폼은 사용자 입력을 검증하고, 필요하면 모델과 연동해서 저장까지 돕는 도구.
forms.py에 정의해둔 UserCreationForm을 활용한다.
forms.py에 정의해두는 경우도 있고, 아니면 간단히 fields = ['username', 'email'] 이런 식으로 쓴 적이 있다.

success_url = reverse_lazy('accountapp:helloworld')
인스턴스를 성공적으로 만들었으면, 리다이렉트할 주소.
reverse_lazy를 사용하는 이유는 클래스 속성이 모듈 임포트 시점에 평가되는데, 이 때 URLConf가 아직 로드 전일 수 있기 때문이다. 파이썬은 파일을 임포트하는 순간 그 파일의 최상단 코드와 클래스 속성을 바로 평가한다. 즉, views.py가 임포트될 때 클래스 속성들도 즉시 평가되는데 이때 URL패턴이 모두 등록되지 않았을 수 있기 때문. 그래서 지연 평가 객체인 reverse_lazy를 활용해서 실제로 URL이 필요할 때 역참조를 수행하게 된다.
쉽게 말하면 모듈 임포트 시점이 아니고, 요청 처리 시점에 참조를 수행하도록 하는 것이 핵심.

accountDetailView

다른 뷰도 보자. 같은 파일에서 detailview를 가져왔다.

class accountDetailView(DetailView, MultipleObjectMixin):
    model = User
    context_object_name = 'target_user'
    template_name = 'accountapp/detail.html'
    paginate_by = 25

    def get_context_data(self, **kwargs):
        object_list = Article.objects.filter(writer=self.get_object())
        return super(accountDetailView, self).get_context_data(object_list = object_list, **kwargs)

우선 이 디테일뷰는 mypage로 들어갔을 때 본인의 계정정보 뿐 아니라 그 유저가 작성한 글도 볼 수 있도록 하는 역할을 해야 한다.

여기서 로그인하지 않은 사람이 접근하더라도 계정의 닉네임, 프로필 메시지, 프로필 사진은 띄워져야 하고, 그 계정이 작성한 글도 볼 수 있어야 한다.

반면 로그인했으면 그 계정의 주인이 접근하는 경우에는 프로필 수정과 글 작성이 가능해야 한다.

MultipleObjectMixin
하나의 뷰에서 목록(QuerySet) 을 다룰 수 있게 해 주는 믹스인.
detailview는 유저같은 단일 객체만을 다루지만, 그 유저의 글 목록을 가져오고 싶은 지금같은 경우에는 MultipleObjectMixin를 함께 상속하여 뷰를 만든다. 파이썬은 다중 상속이 가능하기 때문에 2개의 클래스를 동시에 상속해와서 사용한다고 보면 된다.

context_object_name = 'target_user'
DetailView가 찾은 단일 객체를 템플릿에 어떤 이름으로 넣을지를 지정하는 속성. 지정을 안하면 기본값으로 object가 된다. 이런 식으로 target_user라고 지정해두었다면, 템플릿에서 {{ target_user.username }} 이런 식으로 사용이 가능해진다. 물론 지정했다고 해도 기본값인 object키도 유지가 되기 때문에 {{ object }}라고 써도 접근이 가능하다.

paginate_by = 25
장고의 페이지네이션 기능. MultipleObjectMixin을 같이 상속했기 때문에 쓸 수 있는 속성이다. 한 페이지에 몇 개의 객체를 보일 것인지를 결정하는 속성이라고 보면 됨.

def get_context_data(self, **kwargs):
        object_list = Article.objects.filter(writer=self.get_object())
        return super(accountDetailView, self).get_context_data(object_list = object_list, **kwargs)

이는 템플릿 컨텍스트를 구성, 확장하는 훅이다.
템플릿 컨텍스트란 건 결국 템플릿을 렌더링할 때 사용되는 변수들을 의미함.

self.get_object()라는 건 현재 상세 대상인 User의 인스턴스를 의미한다. 달리 말하면 User라는 테이블의 한 행을 의미한다.
그게 Article의 objects 중에 writer값이 현재 상세대상과 같은 애들만 필터링해서 object_list에 담는 함수라고 볼 수 있음.
즉, 어떤 유저가 작성한 글을 모아오는 기능을 여기서 구현한 것.

articleapp/views.py

ArticleCreateView

@method_decorator(login_required, 'get')
@method_decorator(login_required, 'post')
class ArticleCreateView(CreateView):
    model = Article
    form_class = ArticleCreationForm
    template_name = 'articleapp/create.html'

    def form_valid(self, form):
        temp_article = form.save(commit=False)
        temp_article.writer = self.request.user
        temp_article.save()
        return super().form_valid(form)

    def get_success_url(self):
        return reverse('articleapp:detail', kwargs={'pk': self.object.pk})
def form_valid(self, form):
        temp_article = form.save(commit=False)
        temp_article.writer = self.request.user
        temp_article.save()
        return super().form_valid(form)

POST로 폼을 제출했고, form.is_valid()가 True일 때 호출된다.
form_valid는 원래 FormMixin에 기본 버전이 있고, ModelFormMixin에서 오버라이드되어 저장까지 수행한다. 그런데 CreateView와 UpdateView가 ModelFormMixin을 상속하기 때문에 이런 방식으로 오버라이드가 가능하다.

이는 forms.py에 writer를 저장하지 않기 때문에 임시로 저장하고 억지로 writer값을 주입한 후에 save하는 과정이라고 보면 된다.

    def get_success_url(self):
        return reverse('articleapp:detail', kwargs={'pk': self.object.pk})

form_valid가 끝난 뒤, 어디로 리다이렉트할지 결정할 때 호출된다.

profile
Yoodeit

0개의 댓글