본문 바로가기
IT CHANNEL/Python

[Django] Admin에서 JS를 활용해 동적으로 연관 필드 업데이트하기(데이터 동적 변경)

by TitanX 2023. 8. 19.

 

 

Django Admin에서 html template를 커스텀해 JS를 사용하는 방법을 알아보려고 한다.

간단한 ERD와 요구사항은 다음과 같다.

 

 

 

요구사항

  • Django admin에서 Data 생성 시, 외래키로 2개의 데이터를 설정해야 한다.
  • B 외래키를 설정함에 있어서 A외래키의 영향을 받아 동적으로 변경되어 보여져야 한다.

시도한 방법

  • Django.jquery를 사용하는 방법
    • 경로설정과 세팅에 대한 공수가 커 비효율적이다.
  • admin inner template 활용
    • django admin 에서 add, change 등의 상황마다 template이 내장되어있다.
    • Admin Class 내에서 커스텀 하기 용이한 함수도 있기에 효율적이라고 판단했다.

해결 방법

1. Admin 클래스에서 필요한 데이터 로드 및 변환

 

Introduction 모델의 Admin 클래스에 change_form_template 를 커스텀해야한다.

(기본적으로 admin 의 페이지는 모든 내장 템플릿으로 동작 하기 때문에, 템플릿을 오버라이딩 하여 커스텀이 가능하다.)

여기서 directory는 app/templates/{name}/{something.html} 인 경우 {name}/{something.html} 로 지정한다.

from django.contrib import admin
from .models import Introduction, Career, Company
import json

class IntroductionAdmin(admin.ModelAdmin):
    change_form_template = 'introduction/change_form.html'

    def render_change_form(self, request, context, *args, **kwargs):
        companies = Company.objects.all()
        careers = {
            company.id: [
                {'id': career.id, 'name': career.name} for career in Career.objects.filter(company=company)
            ] for company in companies
        }
        careers_json = json.dumps(careers)
        context['companies'] = companies
        context['careers_json'] = careers_json
        return super().render_change_form(request, context, *args, **kwargs)

admin.site.register(Introduction, IntroductionAdmin)

2. 템플릿에서 JavaScript 로직 작성

 

커스터마이징 할 템플릿에 동적 업데이트 필드를 구현하기 위해 JS를 이용한다.

사용하여 company 필드의 변경을 감지하고, 관련된 career 목록을 동적으로 업데이트합니다.

{% extends "admin/change_form.html" %}
{% block extrahead %}
    {{ block.super }}
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const companyField = document.getElementById('id_company');
            const careerField = document.getElementById('id_career');
            const careersData = JSON.parse('{{ careers_json|escapejs }}');

            if (companyField && careerField) {
                companyField.addEventListener('change', function() {
                    const companyId = parseInt(this.value);
                    const careers = careersData[companyId] || [];
                    careerField.innerHTML = '';
                    careers.forEach(career => {
                        const option = document.createElement('option');
                        option.value = career.id;
                        option.text = career.name;
                        careerField.appendChild(option);
                    });
                });
            }
        });
    </script>
{% endblock %}

 

 

  1. {% extends "admin/change_form.html" %} 로 Django 어드민의 기본 change_form.html 템플릿을 확장한다.
  2. {% block extrahead %} 블록은 <head> 섹션에 추가적인 내용을 삽입할 수 있게 해주며 여기에 JavaScript 를 작성하면된다
  3. DOMContentLoaded 이벤트 리스너: document.addEventListener('DOMContentLoaded', function() {...});는 HTML 문서가 완전히 로드된 후에 코드를 실행하도록 하게해준다.
  4. companyField와 careerField는 각각 회사와 직무의 <select> 엘리먼트를 참조하는데, getElementById를 사용하여 해당 엘리먼트를 선택한다.
  5. const careersData = JSON.parse('{{ careers_json|escapejs }}');는 서버에서 전달한 JSON 형태의 직무 데이터를 JavaScript 객체로 가져온다.
  6. companyField.addEventListener('change', function() {...});는 회사 선택 필드의 값이 변경될 때마다 실행될 함수를 정의한다.

결론

이 방법을 통해 Django Admin에서 동적인 구현이 필요하다면 템플릿 커스텀이 제일 간단하다. Django 에서 실행되는 기능이과 별도의 호출없이 이용할 수 있다.