Javascript에서 django ORM 객체 사용하기

인문학적 개발자 늘한·2020년 10월 12일
1
post-thumbnail

django를 다루는 사람이라면 최소 한 번은 javascript에서 django 객체를 쓰고 싶은 순간이 있었을 겁니다.

먼저 간단한 예를 들어보겠습니다.

#models.py
class Post(models.Model):
    title = models.CharField(max_length=255)
    content = models.TextField()
    hit = models.IntergerField()
#views.py
def post_list(request):
    context = {
    	"posts": Post.objects.all()
    }
    return render(request, "post_list.html", context)

위와 같이 django model 과 view 가 구성되어있을 때,
django template을 통해 javascript에서 값을 쓰고 싶다면,
가장 먼저 드는 생각은 아래와 같을 겁니다.

<!-- post_list.html (before render) -->
<script>
    let posts = {{ posts }}
</script>

하지만 django template은 html 위에 값을 stringfy 시켜서 그대로 그리기 때문에,
아래와 같이 출력될 것입니다.

<!-- post_list.html (after render) -->
<script>
    let posts = <QuerySet [<Post: Post object (1) ... >]>
</script>

이는 javascript 문법과 다르기 때문에 에러가 발생합니다.

이러한 오류를 피하고 django 객체를 javascript에서 사용할 수 있게 해주는 방법은 여러가지가 있지만 저는 아래의 두 방법을 이용합니다.

첫 번째 해결책

위의 오류를 피하기 위해서 아래와 같이 값을 모두 풀어주는 방법을 선택할 수 있습니다.

<!-- post_list.html (before render) -->
<script>
    let posts = [
        {% for post in posts %}
          {
             title: "{{ post.title }}",
             content: "{{ post.content }}",
             hit: {{ post.hit }}
          },
        {% endfor %}
    ]
</script>

결과적으로 아래처럼 랜더링 되기 때문에 javascript 문법에 부합합니다.

<!-- post_list.html (after render) -->
<script>
    let posts = [
        {
           title: "일",
           content: "첫 번째 글입니다.",
           hit: 1
        },
        {
           title: "이",
           content: "두 번째 글입니다.",
           hit: 0
        },
        {
           title: "삼",
           content: "세 번째 글입니다.",
           hit: 0
        },
        {
           title: "사",
           content: "네 번째 글입니다.",
           hit: 0
        },
        {
           title: "오",
           content: "다섯 번째 글입니다.",
           hit: 2
        },
    ]
</script>

위와 같은 방법은 django의 값을 쓸 수 있기는 하지만, 사용할 때마다 값을 풀어줘야 한다는 단점이 있습니다.

두 번째 해결책

두 번째 해결책을 이용하기 위해서 view 와 model을 조금 수정하겠습니다.

#models.py

class Post(models.Model):
    title = models.CharField(max_length=255)
    content = models.TextField()
    hit = models.IntergerField()
    
    def to_json(self):
    	return {
            "title": self.title,
            "content": self.content,
            "hit": self.hit
        }
#views.py
import json


def post_list(request):
    posts = Post.objects.all()
    context = {
    	"posts": posts,
    	"posts_js": json.dumps([post.json() for post in posts])
    }
    return render(request, "post_list.html", context)

model에서는 to_json()이라는 메서드를 생성해서, dictionary 형태로 풀어주었습니다.
그리고 view에서 딕셔너리 형태로 만들어놓은 post 리스트를 json.dumps 로 stringfy 해주었습니다.

굳이 to_json()이라는 임의의 메서드를 생성해서 django query object를 딕셔너리 형태로 바꿔주는 이유는,
딕셔너리는 json.dumps를 통해 json string 포멧으로 변환해줄 수 있기 때문입니다.

이렇게 변환된 값은 javascript에서 아주 간단하게 사용 가능합니다.

<!-- post_list.html -->
<script>
    let posts = JSON.parse("{{ posts_js | escapejs }}")
</script>

json strinfy 되어있는 {{ post_js }} 를 javascript 에서 JSON.parse()를 통해서 풀어주면서 javascript에서 사용할 수 있는 객체로 바꿔주는 것입니다.

이 때 조심해야 할 것은 | escapjs 를 뒤에 꼭 붙여줘야 합니다.

javascript 에서 string으로 입력된 문자열 중에 아래와 같은 문자 포함되어있으면 escape 를 하는 경우가 있기 때문입니다.

# javascript escaping character
{ 
  '&': '&amp;', 
  '<': '&lt;', 
  '>': '&gt;', 
  '"': '&quot;', 
  "'": '&#39;', 
  '/': '&#x2F;', 
  '`': '&#x60;', 
  '=': '&#x3D;' 
}

이 밖에도 drf serializer 등등 여러 방법이 있을 수 있습니다.
위의 두 방법이 비교적 가장 간단하고, 저도 자주 쓰는 방법이기 때문에 소개해드렸습니다.

특히 현재 회사에서 django template에 vuejs를 붙여서 쓰고있기 때문에 두 번째 방법을 유용하게 쓰고있네요.

아무튼 이 글이 헤메고 있던 누군가에게 도움이 되기를 바랍니다.

profile
영어영문학과 출신의 개발자 늘한입니다. Python 을 좋아합니다.

1개의 댓글

comment-user-thumbnail
2023년 3월 8일

감사합니다. 덕분에 막혔던 문제 잘 해결했습니다!

답글 달기