Maison  >  Article  >  développement back-end  >  Comment échanger des données entre Python et JavaScript

Comment échanger des données entre Python et JavaScript

WBOY
WBOYavant
2023-05-11 23:04:041342parcourir

telepath est une bibliothèque Django permettant d'échanger des données entre Python et JavaScript, vous permettant de créer des applications avec de riches interfaces côté client tout en conservant la logique métier dans le code côté serveur.

Que fait-il ?

Il fournit un mécanisme pour regrouper des données structurées comprenant des objets Python dans un format sérialisable JSON. Ce mécanisme peut être étendu pour prendre en charge n'importe quelle classe Python en l'enregistrant auprès de l'implémentation JavaScript correspondante. Les données compressées peuvent ensuite être incluses dans la réponse HTTP et décompressées en JavaScript pour obtenir une structure de données équivalente aux données d'origine.

Installez la méthode

pip install telepath

et ajoutez 'telepath' au INSTALLED_APPS du projet.

Introduction

Supposons que nous construisions une application Django pour jouer aux dames. Nous avons passé des jours ou des semaines à construire une implémentation Python des règles du jeu et à fournir des classes qui représentent l'état actuel du jeu et diverses parties. Cependant, nous souhaitons également fournir au lecteur une interface conviviale, ce qui signifie qu'il est temps pour nous d'écrire un front-end JavaScript. Notre code d'interface utilisateur aura inévitablement ses propres objets représentant différents rôles, reflétant les structures de données que nous suivons sur le serveur - mais nous ne pouvons pas envoyer d'objets Python, donc envoyer ces données au client le fera généralement. Cela signifie concevoir une représentation JSON de l'état du jeu. , et concevoir beaucoup de code de style aux deux extrémités, traversant la structure de données pour effectuer des conversions entre les objets natifs. Voyons comment le télépathe simplifie le processus.

Un jeu de dames complet est un peu trop pour ce tutoriel, nous allons donc simplement opter pour le rendu de cette étape...

Dans l'environnement Python, créez un nouveau projet Django :

pip install "Django>=3.1,<3.2" django-admin startproject draughts cd draughts ./manage.py startapp games

Dans les brouillons/paramètres .py Ajoutez des « jeux » à la liste INSTALLED_APPS.

Pour plus de simplicité, dans cet exemple, nous n'impliquerons pas de base de données et représenterons l'état du jeu comme une classe Python normale au lieu d'un modèle Django. Modifiez games/views.py comme suit :

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(&#39;black&#39;, (x, y))             for y in range(0, 3)             for x in range((y + 1) % 2, 8, 2)         ]         white_pieces = [             Piece(&#39;white&#39;, (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, &#39;game.html&#39;, {})

Créez games/templates/game.html comme suit :

<!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>

Ajoutez de nouvelles vues à drafts/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), ]

Maintenant, utilisez ./manage py runserver démarre le. serveur et accédez à http://localhost:8000/.

Jusqu'à présent, nous avons créé un objet GameState qui représente notre nouveau jeu. Il est maintenant temps d'introduire le télépathe afin que nous puissions transférer cet objet au client. Exécutez la commande suivante :

pip install telepath

et ajoutez 'telepath' à la liste INSTALLED_APPS dans drafts/settings.py. Modifiez maintenant le fichier 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,     })

Ici, JSContext est un outil d'aide utilisé pour gérer la conversion des objets d'état du jeu en une représentation que nous pouvons utiliser en Javascript. js_context.pack prend cet objet et le convertit en une valeur qui peut être sérialisée JSON et transmise à notre modèle. Cependant, le rechargement de la page échoue maintenant avec une erreur du type : je ne sais pas comment emballer l'objet :

C'est parce que GameState est un type Python personnalisé que Telepath ne sait pas encore comment gérer. Tous les types personnalisés transmis à pack doivent être liés à l'implémentation JavaScript correspondante ; cela se fait en définissant un objet Adapter et en l'enregistrant auprès de Telepath. Mettez à jour game/views.py comme suit :

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)

Ici js_constructor est l'identifiant du constructeur JavaScript qui sera utilisé pour construire l'instance GameState sur le client, et js_args définit la liste des arguments qui seront transmis à ce constructeur, pour recréez l'homologue JavaScript de l'objet game_state donné. La classe Media indique un fichier qui suit les conventions de Django pour le formatage des médias et c'est là que se trouve l'implémentation JavaScript de GameState. Nous verrons à quoi ressemble cette implémentation JavaScript plus tard. Pour l'instant, nous devons définir un adaptateur similaire pour la classe Piece, car notre définition du GameStateAdapter dépend de la capacité à empaqueter une instance Piece. Ajoutez la définition suivante à 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)

Rechargez la page et vous verrez l'erreur disparaître, indiquant que nous avons réussi à sérialiser l'objet GameState en JSON et à le transmettre au modèle. Nous pouvons maintenant l'inclure dans le modèle - éditez games/templates/game.html :

<body>        <h2>Draughts</h2>        <div id="game" data-game-state="{{ game_state_json }}">        </div>    </body>

Rechargez à nouveau la page et inspectez l'élément de jeu dans les outils de développement de votre navigateur (dans Chrome et Firefox, cliquez avec le bouton droit sur le commentaire TODO et sélectionnez Inspecter ou Inspecter Element), vous verrez une représentation JSON de l'objet GameState, prête à être décompressée en un objet JavaScript complet.

En plus de conditionner les données dans un format sérialisable JSON, l'objet JSContext suit également les définitions de média JavaScript requises pour décompresser les données en tant que propriétés de média. Mettons à jour la vue du jeu pour la transmettre également au modèle - dans 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,     })

Ajoutez le code suivant au fichier d'en-tête HTML dans 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(&#39;tr&#39;);             table.appendChild(row);             cells[y] = [];             for (let x = 0; x < 8; x++) {                 let cell = document.createElement(&#39;td&#39;);                 row.appendChild(cell);                 cells[y][x] = cell;                 cell.style.width = cell.style.height = &#39;32px&#39;;                 cell.style.backgroundColor = (x + y) % 2 ? &#39;silver&#39;: &#39;white&#39;;             }         }          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,这一点很重要)。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer