telepath 라이브러리 기반 Python과 JavaScript 간 데이터 교환

그것은 어떤 작용을 합니까?


이것은 Python 대상을 포함한 구조화된 데이터를 JSON 서열화 형식으로 묶는 메커니즘을 제공한다.상응하는 JavaScript에 이 메커니즘을 등록함으로써 이 메커니즘을 확장하여 모든 Python 클래스를 지원할 수 있습니다.그리고 압축된 데이터는 HTTP 응답에 포함되고 자바스크립트에서 압축을 풀면 원시 데이터와 같은 효과를 얻을 수 있다.

설치 방법


pip install telepath
프로젝트에 "telepath"를 추가하는 INSTALLED_APPS.

소개


만약 우리가 바둑을 두는 데 사용할 Django 응용 프로그램을 구축하고 있다고 가정해 보세요.우리는 이미 수일 또는 수주의 시간을 들여 게임 규칙의Python 실현을 구축했고 현재 게임 상태와 각 부분을 대표하는 클래스를 제공했다.그러나 우리는 플레이어에 적당한 우호적인 사용자 인터페이스를 제공하기를 희망한다. 이것은 우리가 자바스크립트 전단을 작성할 때가 되었다는 것을 의미한다.우리의 UI 코드는 불가피하게 자신의 대상을 가지게 될 것이다. 이 대상은 서로 다른 역할을 대표하고 우리가 서버에서 추적하고 있는 데이터 구조를 거울로 삼을 수 있다. 그러나 우리는Python 대상을 보낼 수 없기 때문에 이 데이터를 클라이언트에게 보내는 것은 일반적으로 게임 상태를 의미하는 JSON 표시 형식을 의미하고 양쪽에 각각 대량의 스타일 코드를 설계하여 데이터 구조를 옮겨다니며 본 기기의 대상 사이를 왔다 갔다 하는 것을 의미한다.telepath가 이 과정을 어떻게 간소화하는지 봅시다.
완전한 바둑 게임은 본 강좌에 있어서 너무 많기 때문에 우리는 이 한 걸음만 과장하는 것을 선택한다.
Python 환경에서 새 Django 프로젝트를 만듭니다.

pip install "Django>=3.1,<3.2"
django-admin startproject draughts
cd draughts
./manage.py startapp games
draughts/settings.py의 INSTALLED_APPS 목록에'games'를 추가합니다.
간단하게 말하자면, 이 예시에서 우리는 데이터베이스와 관련되지 않고, 게임 상태를 Django모델이 아닌 일반적인Python 클래스로 표시할 것이다.게임/views를 수정합니다.py, 다음과 같습니다.

from django.shortcuts import render


class Piece:
    def __init__(self, color, position):
        self.color = color
        self.position = position


class GameState:
    def __init__(self, pieces):
        self.pieces = pieces

    @staticmethod
    def new_game():
        black_pieces = [
            Piece('black', (x, y))
            for y in range(0, 3)
            for x in range((y + 1) % 2, 8, 2)
        ]
        white_pieces = [
            Piece('white', (x, y))
            for y in range(5, 8)
            for x in range((y + 1) % 2, 8, 2)
        ]
        return GameState(black_pieces + white_pieces)


def game(request):
    game_state = GameState.new_game()

    return render(request, 'game.html', {})
다음과 같이 게임/templates/game을 만듭니다.html:

<!doctype html>
<html>
    <head>
        <title>Draughts</title>
        <script>
            document.addEventListener('DOMContentLoaded', event => {
                const gameElement = document.getElementById('game');
                gameElement.innerHTML = 'TODO: render the board here'
            });
        </script>
    </head>
    <body>
        <h1>Draughts</h1>
        <div id="game">
        </div>
    </body>
</html>
draughts/urls에 새 보기를 추가합니다.py:

from django.contrib import admin
from django.urls import path

from games.views import game

urlpatterns = [
    path('', game),
    path('admin/', admin.site.urls),
]
현재, 사용./manage.pyrunserver에서 서버를 시작하고 http://localhost:8000/에 접근합니다.
지금까지 우리는 새로운 게임을 대표하는 GameState 대상을 만들었습니다. 이 대상을 클라이언트로 전송할 수 있도록telepath를 도입할 때입니다.다음 명령을 수행합니다.

pip install telepath
draughts/settings에'telepath'를 추가합니다.py의 INSTALLED_APPS 목록에 있습니다.현재 게임/views를 편집합니다.py 파일:

import json
from django.shortcuts import render
from telepath import JSContext

# ...

def game(request):
    game_state = GameState.new_game()

    js_context = JSContext()
    packed_game_state = js_context.pack(game_state)
    game_state_json = json.dumps(packed_game_state)

    return render(request, 'game.html', {
        'game_state_json': game_state_json,
    })
여기 JSContext는 Javascript에서 사용할 수 있는 표현 형식으로 게임 상태 대상을 관리하는 데 사용되는 도움말 도구입니다.js_context.pack은 이 대상을 받아들여 JSON을 서열화하고 우리의 템플릿에 전달할 수 있는 값으로 변환합니다.단, 현재 페이지를 다시 불러오는 데 실패했습니다. 다음 형식의 오류가 발생했습니다. don't know how to pack object:
이는 GameState가 Telepath가 아직 어떻게 처리해야 할지 모르는 사용자 정의Python 유형이기 때문이다.pack에 전달되는 모든 사용자 정의 형식은 해당하는 JavaScript 구현에 연결되어야 합니다.이것은 Adapter 객체를 정의하여 telepath에 등록함으로써 이루어집니다.다음과 같이 게임/뷰를 업데이트합니다.py:

import json
from django.shortcuts import render
from telepath import Adapter, JSContext, register

# ...

class GameState:
    # keep definition as before


class GameStateAdapter(Adapter):
    js_constructor = 'draughts.GameState'

    def js_args(self, game_state):
        return [game_state.pieces]

    class Media:
        js = ['draughts.js']


register(GameStateAdapter(), GameState)
여기 js_constructor는 JavaScript 구조 함수의 식별자입니다. 이 식별자는 클라이언트에서 GameState 실례를 구축하는 데 사용되며 js_args는 이 구조 함수에 전달될 매개 변수 목록을 정의하여 주어진game_state 객체의 JavaScript 객체입니다.미디어 클래스 지시 파일입니다. 이 파일은 Django가 포맷 미디어에 대한 약속을 따라 GameState의 JavaScript를 찾을 수 있습니다.잠시 후 이 자바스크립트가 구현된 외관을 볼 수 있습니다. 이제 Piece 클래스에 유사한 어댑터를 정의해야 합니다. 왜냐하면 GameStateAdapter에 대한 정의는 Piece 실례를 포장할 수 있는지에 달려 있기 때문입니다.다음 정의를 게임/views에 추가합니다.py:

class Piece:
    # keep definition as before


class PieceAdapter(Adapter):
    js_constructor = 'draughts.Piece'

    def js_args(self, piece):
        return [piece.color, piece.position]

    class Media:
        js = ['draughts.js']


register(PieceAdapter(), Piece)
페이지를 다시 로드하면 오류 메시지가 사라집니다. 이는 GameState 객체를 JSON으로 정렬하여 템플릿에 전달했음을 나타냅니다.이제 템플릿에 포함할 수 있습니다. - 게임/templates/game 편집.html:

    <body>
        <h1>Draughts</h1>
        <div id="game" data-game-state="{{ game_state_json }}">
        </div>
    </body>
페이지를 다시 불러오고 브라우저의 개발자 도구에서 게임 요소를 검사합니다(크롬과 파이어폭스에서 TODO 주석을 오른쪽 단추로 클릭하고 Inspect 또는 Inspect Element). 게임스테이트 대상을 볼 수 있는 JSON은 완전한 자바스크립트 대상으로 압축을 풀 준비가 되어 있음을 나타냅니다.
데이터를 JSON 서열화 가능한 형식으로 포장하는 것 외에 JSContext 대상은 데이터 압축 해제에 필요한 JavaScript 미디어 정의를 미디어 속성으로 추적합니다.템플릿에도 전달할 게임 보기를 업데이트합니다. - 게임/views에서.py 중:

def game(request):
    game_state = GameState.new_game()

    js_context = JSContext()
    packed_game_state = js_context.pack(game_state)
    game_state_json = json.dumps(packed_game_state)

    return render(request, 'game.html', {
        'game_state_json': game_state_json,
        'media': js_context.media,
    })
다음 코드를 게임/templates/게임에 추가합니다.html의 HTML 헤더 파일:

    <head>
        <title>Draughts</title>
        {{ media }}
        <script>
            document.addEventListener('DOMContentLoaded', event => {
                const gameElement = document.getElementById('game');
                gameElement.innerHTML = 'TODO: render the board here'
            });
        </script>
    </head>
페이지를 다시 불러오고 원본 코드를 보면 자바스크립트 두 개가 포함되어 있습니다.js(클라이언트telepath 라이브러리, 패키지 해제 메커니즘 제공)와 저희가 어댑터 정의에서 지정한draughts.js 파일.후자는 아직 존재하지 않기 때문에 게임스/static/draughts에 있습니다.js에서 만들려면 다음과 같이 하십시오.

class Piece {
    constructor(color, position) {
        this.color = color;
        this.position = position;
    }
}
window.telepath.register('draughts.Piece', Piece);


class GameState {
    constructor(pieces) {
        this.pieces = pieces;
    }
}
window.telepath.register('draughts.GameState', GameState);
이 두 종류의 정의는 우리가 이전에 어댑터 대상에서 성명한 구조 함수를 실현했다. - 구조 함수가 수신한 매개 변수는 js_args가 정의한 매개 변수입니다.window.telepath.register 줄에서 이 클래스 정의를 js_를 통해 추가constructor가 지정한 상응하는 식별자입니다.이제 JSON 압축에 필요한 모든 것을 알 수 있습니다. - 게임/templates/게임으로 돌아가세요.html에서 JS 코드를 업데이트하면 다음과 같습니다.

        <script>
            document.addEventListener('DOMContentLoaded', event => {
                const gameElement = document.getElementById('game');
                const gameStateJson = gameElement.dataset.gameState;
                const packedGameState = JSON.parse(gameStateJson);
                const gameState = window.telepath.unpack(packedGameState);
                console.log(gameState);
            })
        </script>
새 게임/static 폴더를 가져오려면 서버를 다시 시작해야 할 수도 있습니다.페이지를 다시 로드한 다음 브라우저 콘솔에서 Piece 객체가 채워진 GameState 객체를 볼 수 있습니다.현재, 우리는 게임/static/draughts에서 계속할 수 있다.js에서 렌더링 코드를 작성합니다.

class Piece {
    constructor(color, position) {
        this.color = color;
        this.position = position;
    }

    render(container) {
        const element = document.createElement('div');
        container.appendChild(element);
        element.style.width = element.style.height = '24px';
        element.style.border = '2px solid grey';
        element.style.borderRadius = '14px';
        element.style.backgroundColor = this.color;
    }
}
window.telepath.register('draughts.Piece', Piece)


class GameState {
    constructor(pieces) {
        this.pieces = pieces;
    }

    render(container) {
        const table = document.createElement('table');
        container.appendChild(table);
        const cells = [];
        for (let y = 0; y < 8; y++) {
            let row = document.createElement('tr');
            table.appendChild(row);
            cells[y] = [];
            for (let x = 0; x < 8; x++) {
                let cell = document.createElement('td');
                row.appendChild(cell);
                cells[y][x] = cell;
                cell.style.width = cell.style.height = '32px';
                cell.style.backgroundColor = (x + y) % 2 ? 'silver': 'white';
            }
        }

        this.pieces.forEach(piece => {
            const [x, y] = piece.position;
            const cell = cells[y][x];
            piece.render(cell);
        });
    }
}
window.telepath.register('draughts.GameState', GameState)
게임/templates/game.html에render 방법에 대한 호출 추가:

        <script>
            document.addEventListener('DOMContentLoaded', event => {
                const gameElement = document.getElementById('game');
                const gameStateJson = gameElement.dataset.gameState;
                const packedGameState = JSON.parse(gameStateJson);
                const gameState = window.telepath.unpack(packedGameState);
                gameState.render(gameElement);
            })
        </script>
페이지를 다시 불러오면 바둑 프로그램이 준비되어 있어서 게임을 시작할 수 있습니다.
우리는 우리가 이미 얻은 성과를 신속하게 되돌아봅시다.
  • 우리는 사용자 정의Python/JavaScript 형식의 데이터 구조를 포장하고 해독했습니다. 이 구조를 귀속시키기 위해 코드를 작성할 필요가 없습니다.만약 우리의 GameState 대상이 더욱 복잡해진다면 (예를 들어 바둑돌 목록이 바둑돌과 국왕 대상의 혼합 목록이 될 수 있거나 상태가 게임 역사를 포함할 수 있음), 모든 사용 클래스에 어댑터 대상을 제공하는 것을 제외하고는 어떠한 데이터 패키지/해체 논리도 재구성할 필요가 없다
  • 페이지 데이터를 압축하는 데 필요한 JS 파일만 제공합니다. - 만약에 저희 게임 응용 프로그램이 Chess, Go, Othello를 포함하여 확장되고 모든 생성된 클래스가 Telepath를 통해 등록되었다면 저희는 바둑 지식과 관련된 코드만 제공할 수 있습니다
  • 우리가 임의의 대상을 사용하더라도 동적 내연 JavaScript가 필요 없다. 모든 동적 데이터는 JSON 형식으로 전달되고 모든 JavaScript 코드는 배치할 때 고정되어 있다
  • 이상은telepath 라이브러리를 바탕으로 Python과 JavaScript 간의 데이터 교환을 실현하는 상세한 내용입니다. 더 많은 Python과 JavaScript 간의 데이터 교환에 관한 자료는 저희 다른 관련 글을 참고하세요!

    좋은 웹페이지 즐겨찾기