Django - Frequent Code

김기훈·2025년 11월 29일

Django

목록 보기
8/17

Django & DRF


핵심 메서드

  • get_queryset()

    • “이 API가 어떤 데이터를 보여줄지 결정하는 메서드”

      • View에서 반환할 쿼리셋을 동적으로 구성할 때 사용
      • 로그인 유저 기반 필터링, 권한별 필터링, URL 파라미터 기반 필터 등에 활용
      • ViewSet에서는 action(list, retrieve 등)에 따라 다른 결과를 내려줄 수 있음
  • perform_create(serializer)

    • “객체를 저장할 때(POST) 추가 로직을 넣는 메서드”

      • serializer.save() 호출 직전에 추가 처리(작성자 정보, 기본값 설정 등)를 하고 싶을 때 사용
      • 예: 생성 시 자동으로 author=request.user 추가
      • serializer의 create()를 직접 수정하지 않고, view 로직에서 가공할 수 있음
  • perform_update(serializer)

    • “수정(PUT/PATCH) 시 저장 직전에 추가 로직 실행하는 메서드”

      • 로그 기록, 권한 체크, 특정 필드 자동 업데이트 등에 사용
      • View에서 수정 로직을 제어하고 싶을 때 유용
  • perform_destroy(instance)

    • “삭제(DELETE) 시 실제 삭제 전에 커스텀 로직을 넣는 메서드”

      • soft delete(논리 삭제), 로그 남기기, 권한 검증 등을 추가할 때 사용
  • get_serializer_class()

    • “요청 상황에 따라 어떤 Serializer를 사용할지 선택하는 메서드”

      • list와 retrieve에 다른 serializer를 쓰고 싶을 때
      • 회원가입, 로그인, 유저 상세 등 상황별로 serializer가 달라질 때 필수
      • ViewSet에서 action에 따라 serializer 다르게 적용 가능
  • get_permissions()

    • “요청한 action이나 상황에 따라 permission을 동적으로 변경하는 메서드”

      • list는 전체 공개 / create는 로그인 필수 같은 조건 구현
      • 관리자만 수정 가능 등 권한 정책을 세분화할 때 유용
  • get_object()

    • “단일 객체를 가져오는 방식을 커스텀할 때 사용하는 메서드”

      • 권한 체크 강화, soft-delete된 데이터 제외, URL 파라미터 가공 등
      • 기본 lookup 필드(pk → slug 등) 변경 시 사용
  • get_serializer_context()

    • “Serializer 내부에서 사용할 추가 정보를 넘겨주는 메서드”

      • serializer에서 self.context['request'] 같은 값 사용 가능
      • 유저 정보, 쿼리파라미터, 동적 옵션 전달에 활용
  • filter_queryset(queryset)

    • “필터링/검색/정렬 로직을 중앙에서 제어하는 메서드”

      • DRF의 filter_backends가 내부적으로 사용
      • 커스텀 필터나 조건을 통합하고 싶을 때 override

views.py ⭐️

  • import 구문

    • from django.contrib.auth.decorators import login_required
      • 특정 view에 로그인한 사용자만 접근 가능하게 만드는 decorator
      • 로그인 안한 유저가 접근하면 자동으로 로그인 페이지로 redirect됨
    • from django.shortcuts import render, redirect, get_object_or_404
      • render(request, template, context)
        • HTML 템플릿을 렌더링해서 사용자에게 보여줄 때 사용
      • redirect("url_name")
        • 보통 POST 처리 후 Post → Redirect → GET 패턴을 만들기 위해 사용
      • get_object_or_404(Model, 조건...)
        • 조건에 맞는 객체를 찾음 없으면 404 반환

render

  • render(request, template, context=None)

    • Django에서 HTML 템플릿을 불러오고,
    • 데이터(context)를 전달해서 최종 HTML을 만들어 반환하는 함수
      • 즉, 백엔드에서 데이터를 가져와 HTML로 변환해 사용자 브라우저로 보내는 역할
  • request

    • 요청을 보낸 사용자 정보 / 로그인 여부 / 요청 방식(GET/POST) / form 데이터 / 세션 정보
  • "accounts/account_list.html"

    • 두 번째 인자 → 렌더링할 HTML 템플릿 경로
    • Django는 templates/ 폴더에서 찾아 들어감 -> accounts/account_list.html 파일을 렌더링
  • { "accounts": accounts }

    • 템플릿에 전달할 데이터(Context)

    • 템플릿 파일에서 accounts 라는 이름으로 이 데이터를 사용 가능.(아래처럼 사용 가능)

      {% for a in accounts %}
        <p>{{ a.name }}</p>
      {% endfor %}
      
  • render가 하는일 (요약)

    • templates디렉토리accounts/account_list.html 파일을 찾음
    • accounts 라는 이름으로 accounts QuerySet을 템플릿에 전달
    • 템플릿에서 데이터가 삽입된 최종 HTML 생성
    • 그 HTML을 브라우저로 응답(Response) 형태로 반환
      • “템플릿 + 데이터 → 완성된 HTML → 사용자에게 보여줌”

  • render(request, template_name, context=None)
    • Django에서 HTML 템플릿을 불러오고,
    • 데이터(context)를 전달해서 최종 HTML을 만들어 반환하는 함수
      • 즉, 백엔드에서 데이터를 가져와 HTML로 변환해 사용자 브라우저로 보내는 역할
  • request
    • 요청을 보낸 사용자 정보 / 로그인 여부 / 요청 방식(GET/POST) / form 데이터 / 세션 정보
    • 등이 담겨있음
  • "accounts/account_list.html"
    • 두 번째 인자 → 렌더링할 HTML 템플릿 경로
    • Django는 templates/ 폴더에서 찾아 들어감 -> accounts/account_list.html 파일을 렌더링
  • { "accounts": accounts }
    • 템플릿에 전달할 데이터(Context)
    • 템플릿 파일에서 accounts 라는 이름으로 이 데이터를 사용 가능.(아래처럼 사용 가능)

urls.py ⭐️

name

app_name = "accounts"

urlpatterns = [
    path("", views.account_list_view, name="account_list"),
    path("create/", views.account_create_view, name="account_create"),
    path("<int:pk>/delete/", views.account_delete_view, name="account_delete"),
]
  • 1. URL을 직접 쓰지 않고 이름으로 역참조(reverse)할 수 있음

    • 템플릿에서 <a href="/accounts/create/">계좌 생성</a> 이렇게 사용시
      • 나중에 URL을 변경하면 모든 템플릿을 고쳐야 함
    • <a href="{% url 'accounts:account_create' %}">계좌 생성</a>
      • URL이 바뀌어도 name은 그대로이기 때문에 아무것도 고칠 필요 없음.
  • 2. reverse() / redirect() 에서 사용 가능

    • return redirect("/accounts/") -> 직접 작성 -> 불편
    • return redirect("accounts:account_list") -> name로 역참조
  • 3. 대규모 프로젝트에서 URL 충돌을 방지 (namespace 사용)

    • app_name = "accounts" 이렇게 네임스페이스를 붙여두면 다른 앱과 이름이 겹쳐도 충돌X
    • accounts:account_list / transactions:account_list = 충돌X
  • 4. HTML에서 URL 하드코딩이 필요 없어짐

    • <a href="/accounts/1/delete/">삭제</a>
    • <a href="{% url 'accounts:account_delete' pk=account.id %}">삭제</a>

AnalysisListView.as_view() / analysis_list_html

  • Django에서 CBV 와 FBV 를 URL에 연결하는 방식 차이를 보여주는 코드

from django.urls import path
from .views import AnalysisListView, AnalysisCreateView
from .views_html import analysis_list_html, analysis_create_html

app_name = "analysis"

urlpatterns = [
    # API
    path("api/", AnalysisListView.as_view(), name="analysis_list"),
    path("api/create/", AnalysisCreateView.as_view(), name="analysis_create"),

    # HTML
    path("html/", analysis_list_html, name="analysis_html"),
    path("html/create/", analysis_create_html, name="analysis_create_html"),
]
  • AnalysisListView.as_view() | 클래스 기반 뷰(CBV)를 URL에 연결

    • CBV: 뷰를 클래스로 작성하는 방식 (REST APIView, ListAPIView 등)
    • class AnalysisListView(ListAPIView):

      • 클래스이기 때문에 그대로 URL에 연결하면 동작하지 않는다
      • 클래스를 "뷰 함수"로 바꿔주는 메서드가 필요 -> as_view()
    • .as_view()

      • 클래스를 실행 가능한 함수(view 함수)로 변환
      • HTTP 메서드(get/post/put/delete)를 자동으로 매핑
      • DRF/제네릭뷰 기능을 모두 초기화
  • analysis_list_html | 함수 기반 뷰(FBV)를 URL에 연결

    • 그냥 함수이기 때문에 url에 그대로 넣으면 바로 동작
    • def analysis_list_html(request):


include

# config/urls.py

from django.contrib import admin 
from django.urls import path, include 
from accounts import views as account_views 
from transactions import views as transaction_views 

urlpatterns = [ 
path("admin/", admin.site.urls), 
path("accounts/", include('accounts.urls')), 
path("transactions/", include('transactions.urls')), 
]

# accounts/urls.py

from django.urls import path
from .views import AccountListCreateView, AccountDetailView

app_name = 'accounts'

urlpatterns = [
    path('', AccountListCreateView.as_view(), name='account_list'),
    path('<int:pk>/', AccountDetailView.as_view(), name='account_detail'),
]
  • accounts/ 로 들어가면 path('', AccountListCreateView...) 가 실행

    • core/urls.pypath("accounts/", include('accounts.urls'))
      • "accounts 로 들어오는 모든 요청을 accounts.urls 로 넘겨라"
    • 즉, accounts/ 이후의 URL 경로를 accounts/urls.py 로 넘겨서 거기서 다시 해석
      • accounts/ → 남는 경로 = ""
        • path('', AccountListCreateView.as_view()) 가 실행

app_name

# files/urls_html.py

from django.urls import path
from .views_html import file_upload_page, file_list_page, file_delete_page

app_name = "html_files"

urlpatterns = [
    path("upload/", file_upload_page, name="file_upload_page"),
    path("list/", file_list_page, name="file_list_page"),
    path("delete/<int:pk>/", file_delete_page, name="file_delete_page"),
]

# config/urls.py

path("files/", include("files.urls_html"))

# 템플릿에서 호출 방식 -> {% url '네임스페이스:URL네임' %}

{% url 'html_files:file_list_page' %}

request.POST, request.GET

  • request.GET → GET 방식으로 온 데이터(URL 뒤에 ?name=aa&age=20 처럼 붙는 것)

  • request.POST → POST 방식으로 온 데이터(form 내부 input 값들이 보냄)

    • 이 둘은 QueryDict라는 특별한 형태인데, 거의 딕셔너리 처럼 생각해도 됨

request.POST.get() / request.GET.get()

  • get() 이라는 “명령어(메서드)” → 딕셔너리.get("키") 를 그대로 사용하는 것
    • 키가 없어도 에러를 내지 않고 None을 반환하는 것이 장점
      • period = request.GET.get("period", "DAILY") → 기본값지정 가능
data = {"name": "Tom", "age": 20}
data.get("name")  # "Tom"
data.get("job")   # None  ← 에러 안 나고 None

Django에서의 동작

  • name = request.POST.get("name")

    • 사용자가 form에서 <input name="name"> 에 입력한 값을 POST에서 꺼냄
  • period = request.GET.get("period")

    • URL에 ?period=DAILY 등이 붙어 있으면 그 값을 GET에서 꺼냄

analyzers.py

import matplotlib
matplotlib.use("Agg")
  • Agg

    • 서버 환경에서 그래프를 화면에 띄우지 않고 이미지 파일로 생성만 하기 위해 사용하는 필수 설정
    • 웹 서버(Uvicorn, Gunicorn 등)에서는 GUI가 없어서 Agg 백엔드를 지정해야 오류가 안 남

  • 1. class PostForm(forms.ModelForm):

    • from django import forms 필요
  • 2. super.__init__(*args, **kwargs) → 오류 발생

                                def __init__(self, *args, **kwargs):
                                    super().__init__(*args, **kwargs)
    • super().__init__(*args, **kwargs) : 이게 맞는 표현
      • super → super()
profile
안녕하세요.

0개의 댓글