telepath ialah perpustakaan Django untuk bertukar-tukar data antara Python dan JavaScript, membolehkan anda membina aplikasi dengan antara muka sisi klien yang kaya sambil mengekalkan logik perniagaan dalam kod sisi pelayan.
Ia menyediakan mekanisme untuk membungkus data berstruktur, termasuk objek Python, ke dalam format bersiri JSON. Mekanisme ini boleh diperluaskan untuk menyokong mana-mana kelas Python dengan mendaftarkannya dengan pelaksanaan JavaScript yang sepadan. Data yang dibungkus kemudiannya boleh dimasukkan dalam respons HTTP dan dinyahmampatkan dalam JavaScript untuk mendapatkan struktur data yang setara dengan data asal.
pip install telepath
dan tambahkan 'telepath' pada INSTALLED_APPS projek.
Andaikan kita sedang membina apl Django untuk bermain dam. Kami telah menghabiskan beberapa hari atau minggu membina pelaksanaan Python bagi peraturan permainan dan menyediakan kelas yang mewakili keadaan permainan semasa dan pelbagai bahagian. Walau bagaimanapun, kami juga ingin memberikan pemain antara muka mesra pengguna yang betul, yang bermaksud sudah tiba masanya untuk kami menulis bahagian hadapan JavaScript. Kod UI kami pasti akan mempunyai objek sendiri yang mewakili peranan yang berbeza, mencerminkan struktur data yang kami jejaki pada pelayan - tetapi kami tidak boleh menghantar objek Python, jadi menghantar data ini kepada klien biasanya Ini bermakna mereka bentuk perwakilan JSON bagi keadaan permainan , dan mereka bentuk banyak kod penggayaan pada kedua-dua hujung, merentasi struktur data untuk menukar bolak-balik antara objek asli. Mari lihat bagaimana telepath memudahkan proses.
Permainan dam yang lengkap agak terlalu banyak untuk tutorial ini, jadi kami hanya akan memilih langkah pemaparan...
Dalam persekitaran Python, cipta projek Django baharu :
pip install "Django>=3.1,<3.2" django-admin startproject draughts cd draughts ./manage.py startapp games
Tambahkan 'permainan' pada senarai INSTALLED_APPS dalam draf/settings.py.
Untuk kesederhanaan, dalam contoh ini kami tidak akan melibatkan pangkalan data dan mewakili keadaan permainan sebagai kelas Python biasa dan bukannya model Django. Ubah suai permainan/views.py seperti berikut:
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', {})
Cipta permainan/template/game.html seperti berikut:
<!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> <h2>Draughts</h2> <div id="game"> </div> </body> </html>
Tambah pandangan baharu pada draf/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), ]
Sekarang, mulakan pelayan menggunakan ./manage.py runserver dan lawati http://localhost:8000/.
Setakat ini kami telah mencipta objek GameState yang mewakili permainan baharu - kini tiba masanya untuk memperkenalkan telepath supaya kami boleh memindahkan objek itu kepada pelanggan. Jalankan arahan berikut:
pip install telepath
dan tambahkan 'telepath' pada senarai INSTALLED_APPS dalam draf/settings.py. Sekarang edit fail games/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, })
Di sini JSContext ialah alat pembantu yang digunakan untuk menguruskan penukaran objek keadaan permainan kepada perwakilan yang boleh kami gunakan dalam Javascript. js_context.pack mengambil objek itu dan menukarnya kepada nilai yang boleh disiri JSON dan dihantar ke templat kami. Walau bagaimanapun, kini memuat semula halaman gagal dengan ralat borang: tidak tahu cara mengemas objek:
Ini kerana GameState ialah jenis Python tersuai yang Telepath belum tahu cara mengendalikannya. Sebarang jenis tersuai yang dihantar ke pek mesti dipautkan kepada pelaksanaan JavaScript yang sepadan ini dilakukan dengan mentakrifkan objek Penyesuai dan mendaftarkannya dengan telepath. Kemas kini game/views.py seperti berikut:
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)
Di sini js_constructor ialah pengecam pembina JavaScript yang akan digunakan untuk membina contoh GameState pada klien, dan definisi js_args akan diserahkan kepada pembina ini Senarai argumen untuk mencipta semula rakan JavaScript bagi objek game_state yang diberikan. Kelas Media menunjukkan fail yang mengikut konvensyen Django untuk memformat media, dan di mana pelaksanaan JavaScript GameState boleh ditemui. Kita akan melihat rupa pelaksanaan JavaScript ini kemudian, buat masa ini, kita perlu menentukan penyesuai yang serupa untuk kelas Piece, kerana takrifan GameStateAdapter kita bergantung pada keupayaan untuk membungkus contoh Piece. Tambahkan definisi berikut pada games/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)
Muat semula halaman dan anda akan melihat ralat hilang, menunjukkan bahawa kami telah berjaya menyiri objek GameState kepada JSON dan menyerahkannya kepada templat. Sekarang kita boleh memasukkannya ke dalam templat - edit games/templates/game.html:
<body> <h2>Draughts</h2> <div id="game" data-game-state="{{ game_state_json }}"> </div> </body>
Muat semula halaman sekali lagi dan periksa elemen permainan dalam alat pembangun penyemak imbas anda (dalam Chrome dan Firefox , klik kanan TODO komen dan pilih Inspect or Inspect Element), anda akan melihat perwakilan JSON bagi objek GameState, sedia untuk dibongkar ke dalam objek JavaScript yang lengkap.
Selain membungkus data ke dalam format boleh bersiri JSON, objek JSContext juga menjejaki definisi media JavaScript yang diperlukan untuk membongkar data sebagai sifat medianya. Mari kemas kini paparan permainan untuk menghantarnya ke templat juga - dalam games/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, })
Tambahkan kod berikut pada fail pengepala HTML dalam games/templates/game.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>
重新加载页面并查看源代码,您将看到这带来了两个JavaScript包括 —— telepath.js(客户端telepath库,提供解包机制)和我们在适配器定义中指定的draughts.js文件。后者尚不存在,所以让我们在games / 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所需的一切-回到games / templates / game.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>
您可能需要重新启动服务器以获取新的games/static文件夹。重新加载页面,然后在浏览器控制台中,您现在应该看到填充了Piece对象的GameState对象。现在,我们可以继续在games/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)
在games/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代码在部署时都是固定的(如果我们的网站强制执行CSP,这一点很重要)。
Atas ialah kandungan terperinci Bagaimana untuk menukar data antara Python dan JavaScript. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!