![]()
Un logiciel malveillant actif se propage à grande vitesse à travers le registre npm en infectant automatiquement les packages les plus téléchargés du web.
Vous avez peut-être remarqué un petit point rouge tout en bas de mon site, avec un nombre à côté. Il s'agit du nombre de personnes passées sur le site durant la dernière heure.
Je voulais remettre ce truc depuis un bail, bien à l'ancienne comme dans les années 2000 mais sur un site statique, qui plus est sans tracker de stats JS type Google Analytics ou Matomo, et sans cookies de tracking, c'était pas franchement une option... jusqu'à maintenant !
Le compteur, en vrai, tout en bas de korben.info.
La solution évidente sur des sites statiques, c'est souvent un petit Worker Cloudflare qui compte les visiteurs et servirait le chiffre, mais ça se facture à chaque requête. À 390 000 pages vues par jour, ça grimpe vite à des dizaines de millions d'invocations par mois, soit dans les 6 à 7 dollars. C'est pas super cher mais pour un compteur qui n'est utile qu'à satisfaire mon égo tout en sachant s'il y a du monde aujourd'hui, ça ne sert strictement à rien ! Donc le Worker, je l'ai écarté direct.
Du coup j'ai fait ça à l'ancienne. Un petit script Python tourne sur la machine qui héberge le site, une fois par minute. Il pose une seule question à Cloudflare, à savoir combien d'adresses IP distinctes ont chargé une page durant la dernière heure (ou demi-heure, ou quart d'heure ou 5 min, c'est vous qui paramétrez), il écrit la réponse dans un minuscule fichier live.json de quelques centaines d'octets, et c'est tout. Ce fichier, Cloudflare le sert ensuite depuis son cache comme il servirait une image, gratuitement. Zéro Worker, zéro base de données, zéro abonnement, j'ai exactement le même résultat sans dépenser une thune.
Maintenant, faut que je sois clair sur ce que ce chiffre raconte. C'est le nombre de navigateurs distincts ayant chargé une page durant la dernière heure, hors trafic interne de Cloudflare. C'est un "*combien de monde est passé récemment *", pas un "combien lisent là tout de suite".
Et les bots là-dedans ?
En fait, les bots pourris , ceux qui scrapent en boucle, se font bloquer en amont par le Super Bot Fight Mode de Cloudflare, donc ils n'arrivent même pas jusqu'à mon site. Restent les gentils bots, du genre de Googlebot et compagnie, que je laisse passer exprès, parce que les bloquer reviendrait à me flinguer mon référencement. Sur les 7 derniers jours, Cloudflare classe à peine 4% comme bots vérifiés. Donc c'est une goutte d'eau, surtout qu'ils tournent sur une poignée d'IP. Donc oui, mon compteur avale deux ou trois crawlers au passage, mais je ne voulais pas vous mentir en écrivant "zéro bot". Mais l'essentiel c'est que les nuisibles, eux, ne soient pas comptabilisés.
J'ai aussi dû gérer un petit piège car l'API de Cloudflare plafonne sa réponse à 10 000 lignes. Et comme sur une heure entière de trafic ça peut se remplir vite, si je crève ce plafond, la requête sous-compterait sans rien dire. Donc le script lève un flag et le compteur affiche "10 000 personnes ou plus" plutôt qu'un faux nombre. Quand j'y serai aux 10 000 et plus, je pense que je réduirai alors le delta temps en passant de 1h à 30 min...etc.
Côté vie privée, c'est carré également. Cloudflare renvoie à mon serveur une liste JSON des IP passées dans l'heure, mon script en compte le nombre de distinctes, et efface cette liste de sa mémoire dans la milliseconde qui suit. Aucune IP n'est stockée, aucune n'atterrit dans un log, aucun fichier avec les IPs n'est créé sur le disque... Il ne reste qu'un entier. Comme je vous le disais, pas de cookie , pas de traceur, et rien qui touche votre navigateur . Et comme le compteur ne s'affiche jamais en dessous de 10 personnes, impossible d'isoler qui que ce soit.
Maintenant, si vous voulez le même chez vous, sachez que j'ai tout balancé en open source sur GitHub , sous licence MIT, donc servez-vous. Voici les pré-requis :
live_count.py sur votre serveur et vous le lancez une fois par minute (ou toutes les 5 ou 10 min) via un cron.span dans vos pages, tout se règle par des attributs, aucun script inline, donc ça passe même avec une politique de sécurité stricte.Le seul truc sur lequel ne pas vous louper, c'est de mettre un temps de cache court à live.json. Si c'est trop long, tous vos visiteurs verraient un chiffre périmé.
Voilà. Un petit compteur maison simili-live sans tracking et qui ne me coûte rien, c'est le bonheur !
![]()
Afska, le développeur derrière une flopée d'outils pour Game Boy Advance, a eu avec son studio [r]labs une idée géniale ! Ça s'appelle EmuDevz, et c'est un jeu vidéo dans lequel vous devez coder votre propre émulateur NES. Et pas en cliquant sur des boutons comme un loser, hein mais en pissant du vrai code pour le processeur, la puce graphique et la puce audio de la console de Nintendo.
L'émulation, normalement, c'est un truc réputé aride où vous passez des semaines le nez dans la nesdev wiki à piger comment le 6502 gère ses cycles d'horloge. Et pourtant, là, tout devient clair et facile puisque vous avez un éditeur de code intégré façon shell Unix, et vous implémentez le CPU, le PPU et l'APU dans l'ordre que vous voulez. Puis au fil de votre progression, vous débloquez des ROMs et des homebrews à faire tourner dans l'émulateur que vous venez de pondre. Et pour vérifier que votre code tient la route, le jeu fournit même des tests unitaires, des tests vidéo et des tests audio.
Mais le truc qui m'a scotché dans ce "jeu", c'est le debugger. Dans celui-ci, vous avez droit à un visualiseur de mémoire, un log d'instructions, les nametables, la CHR, les sprites, les palettes, les canaux APU affichés un par un, le monitoring du contrôleur... l'arsenal complet du mec qui développe pour de vrai, mais emballé dans une interface gaming. Y'a même un tuto interactif pour apprendre l'assembleur 6502 si vous débarquez sans rien connaître...
Voilà, c'est génial pour apprendre des concepts liés à l'émulation et au développement de jeux retro. On savait déjà qu'il était possible d' apprendre Prolog grâce aux Pokemon ou de recréer une carte 3dfx de zéro et voilà que maintenant, on nous propose carrément de dompter l'émulation de la NES.
Puis surtout, une fois que vous maitriserez un peu plus, le mode libre de EmuDevz vous permettra de commencer à coder des émulateurs pour d'autres systèmes. Ah et y'a même une bande son retro-synthwave dans l'appli pour se mettre dans l'ambiance.
Et je vous l'ai dit ou pas que c'était open source, trouvable sur Steam mais surtout jouable directement depuis votre navigateur ???
La vie est belle ! Et bonne canicule à tous 🥵 !
![]()
Si vous ne connaissez pas, un favicon, c'est cette mini icône pixelisée que vous pouvez voir dans l'onglet de votre navigateur qui en général est le logo du site que vous visitez. Hé bien le dev Tim Wehrle a réussi à y faire tenir un site web entier en stockant tout son HTML dans les 212 octets de l'image.
L'astuce est toute con vous allez voir... Une image, c'est des pixels. Et chaque pixel a 3 valeurs : rouge, vert, bleu, soit 3 octets. Du coup il prend son HTML, le découpe octet par octet, et colle chaque octet dans une couleur. Premier octet dans le rouge, le suivant dans le vert, puis le bleu, et on continue ainsi de suite. Et voilà comment il arrive à stocker 208 octets de HTML dans un carré de 9 pixels sur 9. Et au final, le truc ressemble à des confettis random.
Pour relire tout ça ensuite, un peu de JavaScript suffit. Le navigateur charge l'image, la passe dans un canvas, relit chaque pixel, recolle les octets et réaffiche la page. Il n'y a donc rien à installer et pour tester ça par vous-même, il a même mis une démo ici où vous pouvez regarder l'encodage / décodage se faire sous vos yeux ébahis.
Maintenant, n'espérez pas faire tenir votre site e-commerce là dedans hein puisque la capacité plafonne à 239 octets. Et sans le bout de JavaScript qui décode, le favicon tout seul ne servira à rien. Et ce n'est pas de la stéganographie non plus puisqu'on ne planque rien dans une vraie image. La data c'est l'image...
Mais c'est le genre de hack inutile que j'adore ! Le code est sur GitHub si vous êtes curieux et il raconte toute sa démarche sur son blog .
![]()
Je suis trop content parce qu'avec son nouveau SDK pour extensions , Ableton nous permet enfin d'écrire nos propres outils pour son DAW Live en JavaScript. L'intérêt c'est que ces extensions peuvent lire et modifier vos Sets : pistes, clips, notes MIDI, paramètres, automations... et comme ça votre projet se transforme en un truc qu'on peut trafiquer avec du code (et donc avec de l'IA car ça repose sur des technos web standard ^^ niark niark).
En pratique, une extension peut renommer tous vos clips d'un coup, transformer une photo en mélodie MIDI, découper un beat tout seul, ou carrément faire tourner un petit jeu dans Live. Vous faites un clic droit dans le Set, ça s'exécute, et hop, c'est plié !
Par contre, ça ne remplace pas Max for Live puisque Max tourne en temps réel en agissant sur le son des synthés et des effets alors que les Extensions, elles, se lancent d'un clic droit, font leur boulot, puis s'arrêtent. Ce n'est donc pas du temps réel. C'est juste fait pour automatiser et bidouiller la structure d'un projet mais c'est ce qui fait que les deux se complètent bien.
Screenshot
Pour vous lancer, il faut Node.js, être à l'aise avec le terminal, et toute la doc est sur GitHub . Ableton a même sorti une vidéo qui montre comment créer sa première extension de bout en bout.
J'ai testé en le branchant avec Claude Code, et je lui ai demandé de me faire un morceau french electro du thème d'Indiana Jones et voilà ce que ça m'a sorti from scratch (j'ai juste mis un kit rock pour les drums car sinon, y'avait pas de son sur la piste) :
Maintenant, c'est réservé à Live 12 Suite (Beta), soit le haut de gamme du logiciel, donc sur Standard ou Intro, c'est mort. Ensuite c'est de la bêta, donc c'est pas encore 100% complet... Et n'oubliez pas que c'est du JavaScript tiers qui s'exécute dans Live avec un accès à votre projet, donc ça peut toujours faire des dégâts. Évitez donc d'installer un truc random trouvé sur Discord, car ce serait un peu comme quand vous lanciez des VBScript reçus par mail à la grande époque de Windows 98. Surtout que du code pondu par une IA peut aussi cacher quelques saloperies sans que ça se voie, donc vérifiez toujours d'où ça vient et lisez le code.
Quoi qu'il en soit, si vous êtes sur Live 12 Suite, foncez tester et surtout amusez-vous bien !
![]()
FROST, c'est le nom d'une nouvelle attaque qui transforme votre SSD en mouchard. Des chercheurs de l'université de Graz, avec Daniel Gruss au générique (un des cerveaux derrière Spectre et Meltdown), ont montré qu'un simple site web peut deviner quels autres sites et applis vous avez ouverts et cela juste en mesurant les micro-ralentissements de votre disque. Oui, je sais c'est geudin !
Le principe ?
Quand vous ouvrez la page piégée, elle crée discrètement un gros fichier sur votre disque via une API du navigateur baptisée OPFS ( Origin Private File System ), présente aujourd'hui dans tous les navigateurs modernes. C'est ce fichier qui sert de sonde.
Le JavaScript passe son temps à lire dedans et chronomètre chaque lecture au poil de cul et dès qu'une autre appli ou un autre onglet sollicite le SSD, ça crée un embouteillage minuscule sur le disque... que le code peut repérer sous forme de ralentissement.
Sauf que des variations de timing, ça reste du bruit illisible pour un humain. Du coup les chercheurs ont balancé toutes ces mesures dans un réseau de neurones (un CNN, le même genre de bidule qui reconnaît des choses sur des photos).
Entraîné sur des tonnes de traces, le modèle apprend alors la signature de chaque appli et de chaque site web et voilà comment à partir de ça, il devine ce que vous avez ouvert !
Sur un Mac, dans les tests présentés cette semaine, le truc retrouve le bon site parmi un top 50 dans près de 9 cas sur 10, et grimpe à plus de 95% pour reconnaître les applications ouvertes. Le tout sans la moindre action de votre part, à part avoir cliqué sur le lien. C'est totalement invisible.
Mais avant de débrancher votre PC, renvoyer votre box internet et vous lancer à temps complet dans la culture de chanvre, faut relativiser, car cette attaque a plusieurs limites... Le hic numéro un, c'est que le fichier-sonde doit être énorme, genre 1 Go ou plus, et un site qui se met à bouffer autant de place sur votre disque, ça se remarque vite. Le hic numéro deux, c'est que ce fichier doit être sur le même SSD que le navigateur sinon l'attaque est aveugle.
Les chercheurs ont fait tourner l'attaque complète sur un Mac M2, et démontré que la brique de base fonctionne aussi sous Linux (sans dérouler la classification complète), mais n'ont pas testé Windows. Et surtout, personne n'a encore vu FROST exploité dans la nature.
Perso, je trouve que cette attaque est trop bancale / incertaine pour faire du tracking de masse, en tout cas aujourd'hui...
Pensez donc à fermer les onglets dont vous ne vous servez plus comme ça, y'a moins d'activité à mesurer et pour les plus paranoïaques, vous pouvez toujours vous mettre à surveiller les fichiers OPFS créés par des sites web inconnus. Après comme tout repose sur du JavaScript, bloquer le JS sur les sites pas nets (avec un NoScript ou équivalent) ça coupe aussi l'attaque à la racine.
Les chercheurs proposent aussi aux éditeurs de navigateurs de plafonner la taille de ces fichiers OPFS. Ce serait dans la lignée de ce que fait par exemple Firefox avec le pistage, qui a récemment musclé son anti-fingerprinting .
Bref, pas de panique, personne ne fouille votre SSD en douce mais la technique reste intéressante. Les détails techniques complets sont dans le papier de recherche , qui sera présenté à la conférence DIMVA en juillet. Bonne lecture !
![]()
Distribuer des paquets binaires en WebAssembly, c'est galère. Vous téléchargez le .tar.gz, vous le gunzippez, vous l'extrayez en mémoire... et ça rame sévèrement !! Mais youpi, Jeroen Ooms (qui contribue à webR et bosse chez ROpenSci) vient de publier
tar-vfs-index
, un petit npm package qui casse cette malédiction des enfers en sautant carrément l'étape extraction.
L'astuce est toute bête ! Au lieu d'extraire l'archive, on génère un fichier d'index qui liste la taille et l'offset de chaque fichier dans le tar. Du coup le navigateur n'a plus qu'à monter le blob du tar comme un système de fichiers virtuel, et chaque lecture devient alors un simple slice du blob à la bonne position. Pas d'extraction donc, mais juste du slicing à la demande !
Sous le capot, ça repose sur 3 propriétés alignées. 1/ le format tar est un layout plat : une suite de headers de 512 octets suivis des données du fichier, le tout contigu et adressable à l'octet près. 2/ Emscripten propose un backend filesystem appelé WORKERFS, prévu pour servir les lectures d'un Blob sans le copier dans le heap WASM. Et 3/ les navigateurs ont une API native, DecompressionStream , qui gunzippe efficacement pendant le téléchargement.
Concrètement, vous installez le truc avec npm install tar-vfs-index (y'a aucune dépendance externe) puis vous lancez npx tar-vfs-index archive.tar.gz. Et hop, le package vous sort un JSON dans ce genre :
{
"files": [
{ "filename": "mypackage/DESCRIPTION", "start": 512, "end": 548 },
{ "filename": "mypackage/R/code.R", "start": 1536, "end": 1563 }
],
"remote_package_size": 3072
}
Les valeurs start et end sont tout simplement les offsets dans les données tar décompressées, et remote_package_size indique la taille totale (pour que WORKERFS sache combien préallouer). Ensuite, côté navigateur, ça donne du JavaScript du genre :
const [metaRes, dataRes] = await Promise.all([
fetch('archive.tar.gz.json'),
fetch('archive.tar.gz'),
]);
const metadata = await metaRes.json();
const blob = await new Response(
dataRes.body.pipeThrough(new DecompressionStream('gzip'))
).blob();
FS.mkdir('/pkg');
FS.mount(WORKERFS, { packages: [{ metadata, blob }] }, '/pkg');
Le cas d'usage qui a motivé tout ça, c'est
webR
, le portage du langage R en WebAssembly. Les paquets R sont distribués en .tar.gz et avant cette astuce, charger un paquet voulait dire copier des trucs partout. Maintenant le temps et la mémoire de chargement reviennent à peu près au coût du téléchargement et du gunzip, ce qui est nettement plus léger qu'une extraction complète en RAM. Et ça marche pour n'importe quel bundle distribué en tar.gz : assets de jeu, datasets pour du machine learning, runtimes Python via Pyodide, bref tout ce qui ressemble à une archive lourde côté navigateur !
Y'a également un mode --append qui colle l'index directement à la fin du tarball (le format tar autorise ce genre de bidouille). Ça donne donc un .tar.gz autonome qu'un loader peut monter sans aller chercher un fichier de métadonnées séparé !
Bref, c'est plutôt joli comme façon de faire et ça vaut le coup d'œil si vous trimballez du tar.gz dans du WebAssembly.
![]()
Modern web design has turned websites from static and boring walls of information into ways of providing fun and engaging experiences to the user. Our new ‘More web’ project path shows young creators how to add interaction and animation to a webpage through JavaScript code.

As of 2024, JavaScript is the most popular programming language in the world. And it’s easy to see why when you look at its versatility and how it can be used to create dynamic and interactive content on websites. JavaScript lets you handle events and manipulate HTML and CSS so that you can build everything from simple animations, to forms that can be checked for missing or nonsensical answers. If you’ve ever seen a webpage continuously load more content when you reach the end, that’s JavaScript.

The six new projects in the ‘More web’ path move learners beyond the basics of HTML and CSS encountered in our ‘Introduction to web’ path. Young people will explore what JavaScript makes possible in web development, with plenty of support along the way.
By the end of the ‘More web’ path, learners will have covered the following key programming concepts:
| HTML and CSS | JavaScript |
| Navbars, grid layouts, hero images and image sliders Form design and handling user input Accessibility and responsive design Sizing elements relative to the viewport or container Creating parallax scrolling effects using background-attachment Fixing the position of elements and using z-index to layer elements | Local and global variables, and constants Selection (if, else if, and else) Repetition (for loops) Using Console log Concatenation using template literals Event listeners Use of the intersection observer API to animate elements and lazy-load images Use of the localStorage object to retain user preferencesWriting and calling functions to take advantage of the Document Object Model (DOM) Use setTimeout() to create time delaysWork with Date() functions |
We’ve designed the path to be completed in six one-hour sessions, with one hour per project. However, learners can work at their own speed and the project instructions invite them to take additional time to upgrade their projects if they wish.
All six projects use our Code Editor, which has been tailored specifically to young people’s needs. This integrated development environment (IDE) helps make learning text-based programming simple, safe, and accessible. The projects include starter code, handy code snippets, and images to help young people build their websites.

The path follows our Digital Making Framework, with its deliberate format of six projects that become less structured as learners progress. The Explore projects at the start of the path are where the initial learning takes place. Learners then develop their new skills by putting them into practice in the Design and Invent projects, which encourage them to use their imagination and make projects that matter to them.

Learners use HTML and CSS to design a website that lets people discover a place they may never get a chance to visit — Antarctica. They discover how to create a navigation bar (or navbar), set accessible colours and fonts, and add a responsive grid layout to hold beautiful images and interesting facts about this fascinating continent.

In the second Explore project, young people build an interactive website where the user can design a superhero character. Learners use JavaScript to let the user change the text on their website, show and hide elements, and create a hero image slider. They also learn how to let the user set the colour theme for the site and keep their preferences, even if they reload the page.

Young people create an interactive story with animated text and characters that are triggered when the user scrolls. They will learn how to design for accessibility and improve browser performance by only loading images when they’re needed.

This is where learners can practise their skills and bring in their own interests to make a fan website, which lets a user make choices that change the webpage content.

The final Design project invites young people to build a personalised web app that lets users test what they know about a topic. Learners choose a topic for their quiz, create and animate their questions, and then show the user their final score. They could make a quiz about history, nature, world records, science, sports, fashion, TV, movies… or anything else they’re an expert in!

In this final project, young people bring everything they’ve learnt together and use their new coding powers and modern design skills to create an interactive website to share a part of their world with others. They could provide information about their culture, interests, hobbies or expertise, share fun facts, create quizzes, or write reviews. Learners consider what makes a website useful and informative, as well as fun and accessible.
Encourage your young learners to take their next steps in web design, learn JavaScript, and try out this new path of coding projects to create interactive websites that excite and engage users.

Young people can also enter one of their Design or Invent projects into the Web category of the yearly Coolest Projects showcase by taking a short video showing the project and the code used to make it. Their creation will become part of the Coolest Projects online gallery for people all over the world to see!
The post Get young people making interactive websites with JavaScript and our ‘More web’ path appeared first on Raspberry Pi Foundation.
Programming is becoming an increasingly useful skill in today’s society. As we continue to rely more and more on software and digital technology, knowing how to code is also more and more valuable. That’s why many parents are looking for ways to introduce their children to programming. You might find it difficult to know where to begin, with so many different kids’ coding languages and platforms available. In this blog post, we explore how children can progress through different programming languages to realise their potential as proficient coders and creators of digital technology.

Everyone needs to start somewhere, and one great option for children aged 5–7 is ScratchJr (Scratch Junior), a visual programming language with drag-and-drop blocks for creating simple programs. ScratchJr is available for free on Android and iOS mobile devices. It’s great for introducing young children to the basics of programming, and they can use it to create interactive stories and games.
Moving on from ScratchJr, there’s its web-based sibling Scratch. Scratch offers drag-and-drop blocks for creating programs and comes with an assortment of graphics, sounds, and music for your child to bring their programs to life. This visual programming language is designed specifically for children to learn programming fundamentals. Scratch is available in multiple spoken languages and is perfect for beginners. It allows kids to create interactive stories, animations, and games with ease.
The Raspberry Pi Foundation has a wealth of free Scratch resources we have created specifically for young people who are beginners, such as the ‘Introduction to Scratch’ project path. And if your child is interested in physical computing to interact with the real world using code, they can also learn how to use electronic components, such as buzzers and LEDs, with Scratch and a Raspberry Pi computer.

Another fun option for children who want to explore coding and physical computing is the micro:bit. This is a small programmable device with an LED display, buttons, and sensors, and it can be used to create games, animations, interactive projects, and lots more. To control a micro:bit, a visual programming language called MakeCode can be used. The micro:bit can also be programmed using Scratch or text-based languages such as Python, offering an easy transition for children as their coding skills progress. Have a look at our free collection of micro:bit resources to learn more.
Everyone is familiar with websites, but fewer people know how they are coded. HTML is a markup language that is used to create the webpages we use every day. It’s a great language for children to learn because they can see the results of their code in real time, in their web browser. They can use HTML and CSS to create simple webpages that include links, videos, pictures, and interactive elements, all the while learning how websites are structured and designed. We have many free web design resources for your child, including a basic ‘Introduction to web development’ project path.

If your child is becoming confident with Scratch and HTML, then using Python is the recommended next stage in their learning. Python is a high-level text-based programming language that is easy to read and learn. It is a popular choice for beginners as it has a simple syntax that often reads like plain English. Many free Python projects for young people are available on our website, including the ‘Introduction to Python’ path.

The Python community is also really welcoming and has produced a myriad of online tutorials and videos to help learners explore this language. Python can be used to do some very powerful things with ease, which is why it is so popular. For example, it is relatively simple to create Python programs to engage in machine learning and data analysis. If you wanted to explore large language models such as GPT, on which the ChatGPT chatbot is based, then Python would be the language of choice.
JavaScript is the language of the web, and if your child has become proficient in HTML, then this is the next language for them. JavaScript is used to create interactive websites and web applications. As young people become more comfortable with programming, JavaScript is a useful language to progress to, given how ubiquitous the web is today. It can be tricky to learn, but like Python, it has a vast number of libraries of functions that people have already created for it to achieve things more quickly. These libraries make JavaScript a very powerful language to use.
There are many different programming languages, and each one has its own strengths and weaknesses. Some are easy to learn and use, some are really fast, and some are very secure.

Starting with visual languages such as Scratch or MakeCode allows your child to begin to understand the basic concepts of programming without needing any developed reading and keyboard skills. Once their understanding and skills have improved, they can try out text-based languages, find the one that they are comfortable with, and then continue to learn. It’s fairly common for people who are proficient in one programming language to learn other languages quite quickly, so don’t worry about which programming language your child starts with.
Whether your child is interested in working in software development or just wants to learn a valuable — and creative — skill, helping them learn to code and try out different kids’ coding languages is a great way for you to open up new opportunities for them.
The post Kids’ coding languages appeared first on Raspberry Pi Foundation.
When you develop a web app or a mobile app with Django, it is common to use the Django REST Framework for communication with the server-side. The client-side makes GET, POST, PUT, and DELETE requests to the REST API to read, create, update, or delete data there. The communication by Ajax is pretty uncomplicated, but how would you upload an image or another file to the server? I will show you that in this article by creating user avatar upload via REST API. Find the full code for this feature on Github.
We will start by installing Pillow for image handling to the virtual environment using the standard pip command:
(venv)$ pip install PillowCreate accounts app with a custom User model:
# myproject/apps/accounts/models.py
import os
import sys
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
def upload_to(instance, filename):
now = timezone.now()
base, extension = os.path.splitext(filename.lower())
milliseconds = now.microsecond // 1000
return f"users/{instance.pk}/{now:%Y%m%d%H%M%S}{milliseconds}{extension}"
class User(AbstractUser):
# …
avatar = models.ImageField(_("Avatar"), upload_to=upload_to, blank=True)You can add there as many fields as you need, but the noteworthy part there is the avatar field.
Update the settings and add the accounts app to INSTALLED_APPS, set the AUTH_USER_MODEL, and the configuration for the static and media directories:
# myproject/settings.py
INSTALLED_APPS = [
# …
"myproject.apps.accounts",
]
AUTH_USER_MODEL = "accounts.User"
STATICFILES_DIRS = [os.path.join(BASE_DIR, "myproject", "site_static")]
STATIC_ROOT = os.path.join(BASE_DIR, "myproject", "static")
STATIC_URL = "/static/"
MEDIA_ROOT = os.path.join(BASE_DIR, "myproject", "media")
MEDIA_URL = "/media/"Next small steps:
makemigrations and migrate management commands.createsuperuser management command.Install Django REST Framework for the REST APIs to your virtual environment, as always, using pip:
(venv)$ pip install djangorestframeworkWe'll be using authentication by tokens in this example. So add Django REST Framework to INSTALLED_APPS in the settings and set TokenAuthentication as the default authentication in the REST_FRAMEWORK configuration:
# myproject/settings.py
INSTALLED_APPS = [
# …
"rest_framework",
"rest_framework.authtoken",
# …
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
]
}In Django REST Framework, serializers are used for data validation, rendering, and saving. They are similar to Django forms. Prepare UserAvatarSerializer for avatar uploads:
# myproject/apps/accounts/serializers.py
from django.contrib.auth import get_user_model
from rest_framework.serializers import ModelSerializer
User = get_user_model()
class UserAvatarSerializer(ModelSerializer):
class Meta:
model = User
fields = ["avatar"]
def save(self, *args, **kwargs):
if self.instance.avatar:
self.instance.avatar.delete()
return super().save(*args, **kwargs)Now create an API view UserAvatarUpload for avatar uploads.
# myproject/apps/accounts/views.py
from rest_framework import status
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from .serializers import UserAvatarSerializer
class UserAvatarUpload(APIView):
parser_classes = [MultiPartParser, FormParser]
permission_classes = [IsAuthenticated]
def post(self, request, format=None):
serializer = UserAvatarSerializer(data=request.data, instance=request.user)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)Make sure that the view uses MultiPartParser as one of the parser classes. That's necessary for the file transfers.
In the URL configuration, we will need those URL rules:
TemplateView.# myroject/urls.py
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path
from django.views.generic import TemplateView
from django.conf import settings
from myproject.accounts.views import UserAvatarUpload
from rest_framework.authtoken.views import obtain_auth_token
urlpatterns = [
path("", TemplateView.as_view(template_name="index.html")),
path("api/auth-token/", obtain_auth_token, name="rest_auth_token"),
path("api/user-avatar/", UserAvatarUpload.as_view(), name="rest_user_avatar_upload"),
path("admin/", admin.site.urls),
]
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)I will illustrate the frontend using Bootstrap HTML and Vanilla JavaScript. Of course, you can implement the same using ReactJS, Vue, Angular, or other JavaScript framework and any other CSS framework.
The template for the index page has one login form with username and password or email and password fields (depending on your implementation), and one avatar upload form with a file selection field. Also, it includes a JavaScript file avatar.js for Ajax communication.
{# myproject/templates/index.html #}
<!doctype html>
{% load static %}
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<title>Hello, World!</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-8">
<p class="text-muted my-3"><small>Open Developer Console for information about responses.</small></p>
<h1 class="my-3">1. Log in</h1>
<form id="login_form">
<div class="form-group">
<label for="id_email">Email address</label>
<input type="email" class="form-control" id="id_email" aria-describedby="emailHelp"
placeholder="Enter email"/>
</div>
<div class="form-group">
<label for="id_password">Password</label>
<input type="password" class="form-control" id="id_password" placeholder="Password"/>
</div>
<button type="submit" class="btn btn-primary">Log in</button>
</form>
<h1 class="my-3">2. Upload an avatar</h1>
<form id="avatar_form">
<div class="form-group">
<label for="id_avatar">Choose an image for your avatar</label>
<input type="file" class="form-control-file" id="id_avatar"/>
</div>
<button type="submit" class="btn btn-primary">Upload</button>
</form>
</div>
</div>
</div>
<script src="{% static 'site/js/avatar.js' %}"></script>
</body>
</html>Last but not least, create the JavaScript file avatar.js. It contains these things:
// myproject/site_static/site/js/avatar.js
let userToken;
document.getElementById('login_form').addEventListener('submit', function(event) {
event.preventDefault();
let email = document.getElementById('id_email').value;
let password = document.getElementById('id_password').value;
fetch('http://127.0.0.1:8000/api/auth-token/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
"username": email,
"password": password,
})
}).then( response => {
return response.json();
}).then(data => {
console.log(data);
userToken = data.token;
console.log('Logged in. Got the token.');
}).catch((error) => {
console.error('Error:', error);
});
});
document.getElementById('avatar_form').addEventListener('submit', function(event) {
event.preventDefault();
let input = document.getElementById('id_avatar');
let data = new FormData();
data.append('avatar', input.files[0]);
fetch('http://127.0.0.1:8000/api/user-avatar/', {
method: 'POST',
headers: {
'Authorization': `Token ${userToken}`
},
body: data
}).then(response => {
return response.json();
}).then(data => {
console.log(data);
}).catch((error) => {
console.error('Error:', error);
});
});In the JavaScript file, we are using fetch API for the REST API requests. The noteworthy part there is the FormData class that we use to send the file to the server.
Now run the local development server and go to the http://127.0.0.1:8000. There you will have something like this:
![]()
As more than a half Internet usage happens on mobile devices, there is a demand to switch from usual HTML websites and platforms to mobile apps. Whether you create a native mobile app, a hybrid app, or Progressive Web App, you will likely have to communicate with the server via REST API or GraphQL. It is pretty clear how to transfer textual data from and to a remote server. But after this exercise, we can also transfer binary files like images, PDF or Word documents, music, and videos.
Happy coding!
Cover Photo by Dan Silva
From time to time I google for the right syntax how to process lists and dictionaries in Python or arrays and objects in JavaScript. So I decided to extend my series of equivalents with those functions. After all, it's me too, who will be using the information I provide here.
Sometimes we need to check from a list of conditions if all of them are true, or from a list of elements if all of them are not empty.
This can be checked with the following in Python:
items = [1, 2, 3]
all_truthy = all(items)
# TrueAnd here is an equivalent in JavaScript:
items = [1, 2, 3];
all_truthy = items.every(Boolean);
// trueSimilarly, we can check if at least one of the conditions is true, or there is at least one non-empty element in a list.
It Python we would do that with:
items = [0, 1, 2, 3]
some_truthy = any(items)
# TrueAnd in JavaScript we would check it like this:
items = [0, 1, 2, 3];
some_truthy = items.some(Boolean);
// trueHere is an example of how to iterate through a list of items and also check their indices in Python. It is useful for verbose console output when creating different command line tools that process data:
items = ['a', 'b', 'c', 'd']
for index, element in enumerate(items):
print(f'{index}: {element};')In JavaScript an analogous way to do the same would be using the forEach() method. The usual for loop is also an option, but I find the forEach() more elegant and clear.
items = ['a', 'b', 'c', 'd'];
items.forEach(function(element, index) {
console.log(`${index}: ${element};`);
});To process all elements of a list, you can either iterate through them with the for loop and create a new list with modifications, or you can do that in one step by mapping the list items to a modification function. In Python this can be done with the map() function:
items = [0, 1, 2, 3]
all_doubled = list(map(lambda x: 2 * x, items))
# [0, 2, 4, 6]In JavaScript the map() is a method of an array:
items = [0, 1, 2, 3];
all_doubled = items.map(x => 2 * x);
// [0, 2, 4, 6]When you need to search for some elements in a list or array and want to avoid for loops, you can use the filtering functionality. In Python that is doable with the filter() function that accepts the filtering function and the list and returns a new filtered list.
items = [0, 1, 2, 3]
only_even = list(filter(lambda x: x % 2 == 0, items))
# [0, 2]In JavaScript there is a filter() method of the array for that.
items = [0, 1, 2, 3];
only_even = items.filter(x => x % 2 === 0);
// [0, 2]In both cases, the filtering function checks each item if it is matching the filter criteria and returns true in that case.
When you want to apply some function to a list of items to get a single result in one go, you can use the reduce function. It works for summing, multiplying, ORing, ANDing, or checking maximums and minimums.
In Python there is a reduce() function for that.
from functools import reduce
items = [1, 2, 3, 4]
total = reduce(lambda total, current: total + current, items)
# 10In JavaScript there is a reduce() method of the array.
items = [1, 2, 3, 4];
total = items.reduce((total, current) => total + current);
// 10There are multiple ways to merge dictionaries in Python or objects in JavaScript. But these are probably the simplest ones.
In Python it's decomposing dictionaries to tuples of keys and arrays, joining them, and creating a new dictionary.
d1 = {'a': 'A', 'b': 'B'}
d2 = {'a': 'AAA', 'c': 'CCC'}
merged = dict(list(d1.items()) + list(d2.items()))
# {'a': 'AAA', 'b': 'B', 'c': 'CCC'}Analogously, in JavaScript it's spreading two objects into a new object:
d1 = {a: 'A', b: 'B'}
d2 = {a: 'AAA', c: 'CCC'}
merged = {...d1, ...d2};
// {a: 'AAA', b: 'B', c: 'CCC'}all() / every(), any() / some(), map(), filter(), and reduce() are there to help you.Of course, I also updated the cheat sheet with the full list of equivalents in Python and JavaScript that you saw here described. This cheat sheet helps me with a good overview next to my laptop, so I believe that it would be helpful to you too. The new revision 10 is with syntax highlighting, so it makes it even better to explore and understand.
Get the Ultimate Cheat Sheet of Equivalents in Python and JavaScript
Use it for good!
Cover photo by Darren Chan.
A couple of months ago the third release of Django Cookbook was published under the title Django 2 Web Development Cookbook - Third Edition. This edition was thoroughly and progressively re-written by Jake Kronika, the guy who had reviewed my second edition and had added a lot of value to it. I was sure that he wouldn't disappoint the readers, so I invited him to write the update. In this article, I will guide you through the main new highlights of over 500 pages of this new book.
Just like William S. Vincent's books, Django 2 Web Development Cookbook - Third Edition is adapted to Django 2.1 and Python 3.6. So you will be dealing with the state of the art technologies building your Django projects. Unicode strings, f-strings, super() without parameters, HTML5 tags, and object-oriented JavaScript to mention a few are used all over the book. The code is carefully generalized and even more adapted to the Don't-Repeat-Yourself (DRY) principle.
Docker is one of the most popular deployment technologies and Jake gives a good compact introduction how to use it with Django.
12-factor app guidelines suggest saving app configuration in environment variables. In the book, there is a practical example of how to use it.
I introduced multilingual fields in previous editions of the book, but there they had a limitation, that region-specific languages like Australian English or Swiss German were not supported. Now they are!
Schema.org Microdata allows you to define the context of the content more specifically so that the content is more machine-readable. This was new to me and I still don't know the exact practical value of it, but I guess it is related to accessibility, new ways of presenting data via plugins, and Artificial Intelligence.
Since Django 1.11 form fields are rendered using templates instead of Python code and those templates can be customized. There is a recipe that shows you how to do that.
HTML5 has the <picture> tag with <source> children that can be used in combination with the sorl-thumbnail Python package to generate different versions of the image based on your viewport size: load small image on the mobile, middle image on the tablet, and big image on the desktop or smart TV.
In my previous editions, I only showed how to upload a file by Ajax and attach it to a Django model. In Jake's update, it is shown how you can also delete the image.
Since Django 1.11 you can define special requirements for the passwords of your users, for example, have a mix of small and big letters or include at least 1 number and 3 special characters, etc. There is a practical recipe how to do that.
When it comes to branding or copyright protection, it is common to add special watermarks, semitransparent images on top of your normal pictures. Jake added an example, how to do that and it was very interesting to me.
In one recipe it is shown how to login to a Django website using Auth0, which seems to be a passwordless authentication system with integrations of popular connection services like OpenID Connect, Facebook, Google, Github, LinkedIn, PayPal, Yahoo!, and others. I haven't tried that myself yet, but it can be an interesting option for a social website.
It is common to cache websites using Memcached service, but a good alternative is caching with Redis and django-redis. Moreover, you can easily save user sessions there.
In the previous editions, I introduced django-mptt for creating hierarchical structures. However, recently many projects are moving towards its alternative - django-treebeard which has more stability and writing speed. In the book, Jake shows you how to work with it.
There was a lot of new things to learn. For example, for me personally Docker usage was new, and I haven't heard of schema.org microdata and Auth0 which were introduced in this book. All in all, I think, Jake Kronika did an enormous job with this update and it's really worth purchasing this book, especially as there is a winter-holidays sale where you can get the EPUB, MOBI, and PDF with the code examples just for ~ 5 €.
Have a nice Christmas time and come back to this blog next year!
Cover photo by chuttersnap.
In the last three parts of the series of articles about analogies in Python and JavaScript, we explored lots of interesting concepts like serializing to JSON, error handling, using regular expressions, string interpolation, generators, lambdas, and many more. This time we will delve into function arguments, creating classes, using class inheritance, and defining getters and setters of class properties.
Python is very flexible with argument handling for functions: you can set default values there, allow a flexible amount of positional or keyword arguments (*args and **kwargs). When you pass values to a function, you can define by name to which argument that value should be assigned. All that in a way is now possible in JavaScript too.
Default values for function arguments in Python can be defined like this:
from pprint import pprint
def report(post_id, reason='not-relevant'):
pprint({'post_id': post_id, 'reason': reason})
report(42)
report(post_id=24, reason='spam')In JavaScript that can be achieved similarly:
function report(post_id, reason='not-relevant') {
console.log({post_id: post_id, reason: reason});
}
report(42);
report(post_id=24, reason='spam');Positional arguments in Python can be accepted using the * operator like this:
from pprint import pprint
def add_tags(post_id, *tags):
pprint({'post_id': post_id, 'tags': tags})
add_tags(42, 'python', 'javascript', 'django')In JavaScript positional arguments can be accepted using the ... operator:
function add_tags(post_id, ...tags) {
console.log({post_id: post_id, tags: tags});
}
add_tags(42, 'python', 'javascript', 'django'); Keyword arguments are often used in Python when you want to allow a flexible amount of options:
from pprint import pprint
def create_post(**options):
pprint(options)
create_post(
title='Hello, World!',
content='This is our first post.',
is_published=True,
)
create_post(
title='Hello again!',
content='This is our second post.',
)A common practice to pass multiple optional arguments to a JavaScript function is through a dictionary object, for example, options.
function create_post(options) {
console.log(options);
}
create_post({
'title': 'Hello, World!',
'content': 'This is our first post.',
'is_published': true
});
create_post({
'title': 'Hello again!',
'content': 'This is our second post.'
});
Python is an object-oriented language. Since ECMAScript 6 standard support, it's also possible to write object-oriented code in JavaScript without hacks and weird prototype syntax.
In Python you would create a class with the constructor and a method to represent its instances textually like this:
class Post(object):
def __init__(self, id, title):
self.id = id
self.title = title
def __str__(self):
return self.title
post = Post(42, 'Hello, World!')
isinstance(post, Post) == True
print(post) # Hello, World!In JavaScript to create a class with the constructor and a method to represent its instances textually, you would write:
class Post {
constructor (id, title) {
this.id = id;
this.title = title;
}
toString() {
return this.title;
}
}
post = new Post(42, 'Hello, World!');
post instanceof Post === true;
console.log(post.toString()); // Hello, World!Now we can create two classes Article and Link in Python that will extend the Post class. Here you can also see how we are using super to call methods from the base Post class.
class Article(Post):
def __init__(self, id, title, content):
super(Article, self).__init__(id, title)
self.content = content
class Link(Post):
def __init__(self, id, title, url):
super(Link, self).__init__(id, title)
self.url = url
def __str__(self):
return '{} ({})'.format(
super(Link, self).__str__(),
self.url,
)
article = Article(1, 'Hello, World!', 'This is my first article.')
link = Link(2, 'DjangoTricks', 'https://djangotricks.blogspot.com')
isinstance(article, Post) == True
isinstance(link, Post) == True
print(link)
# DjangoTricks (https://djangotricks.blogspot.com)In JavaScript the same is also doable by the following code:
class Article extends Post {
constructor (id, title, content) {
super(id, title);
this.content = content;
}
}
class Link extends Post {
constructor (id, title, url) {
super(id, title);
this.url = url;
}
toString() {
return super.toString() + ' (' + this.url + ')';
}
}
article = new Article(1, 'Hello, World!', 'This is my first article.');
link = new Link(2, 'DjangoTricks', 'https://djangotricks.blogspot.com');
article instanceof Post === true;
link instanceof Post === true;
console.log(link.toString());
// DjangoTricks (https://djangotricks.blogspot.com)In object oriented programming, classes can have attributes, methods, and properties. Properties are a mixture of attributes and methods. You deal with them as attributes, but in the background they call special getter and setter methods to process data somehow before setting or returning to the caller.
The basic wireframe for getters and setters of the slug property in Python would be like this:
class Post(object):
def __init__(self, id, title):
self.id = id
self.title = title
self._slug = ''
@property
def slug(self):
return self._slug
@slug.setter
def slug(self, value):
self._slug = value
post = new Post(1, 'Hello, World!')
post.slug = 'hello-world'
print(post.slug)In JavaScript getters and setters for the slug property can be defined as:
class Post {
constructor (id, title) {
this.id = id;
this.title = title;
this._slug = '';
}
set slug(value) {
this._slug = value;
}
get slug() {
return this._slug;
}
}
post = new Post(1, 'Hello, World!');
post.slug = 'hello-world';
console.log(post.slug);As you might have noticed, I am offering a cheat sheet with the full list of equivalents in Python and JavaScript that you saw here described. At least for me, it is much more convenient to have some printed sheet of paper with valuable information next to my laptop, rather than switching among windows or tabs and scrolling to get the right piece of snippet. So I encourage you to get this cheat sheet and improve your programming!
Use it for good!
Cover photo by Andre Benz
This is the 3rd part of 4-article series about analogies in Python and JavaScript. In the previous parts we covered a large part of the traditional Python 2.7 and JavaScript based on the ECMAScript 5 standard. This time we will start looking into Python 3.6 and JavaScript based on the ECMAScript 6 standard. ECMAScript 6 standard is pretty new and supported only the newest versions of browsers. For older browsers you will need Babel to compile your next-generation JavaScript code to the cross-browser-compatible equivalents. It opens the door to so many interesting things to explore. We will start from string interpolation, unpacking lists, lambda functions, iterations without indexes, generators, and sets!
The old and inefficient way to build strings with values from variables is this concatenation:
name = 'World'
value = 'Hello, ' + name + '!\nWelcome!'This can get very sparse and difficult to read. Also it is very easy to miss whitespaces in the sentence around variables.
Since Python version 3.6 and JavaScript based on the ECMAScript 6 standard, you can use so called string interpolation. These are string templates which are filled in with values from variables.
In Python they are also called f-string, because their notation starts with letter "f":
name = 'World'
value = f"""Hello, {name}!
Welcome!"""
price = 14.9
value = f'Price: {price:.2f} €' # 'Price: 14.90 €'In JavaScript string templates start and end with backticks:
name = 'World';
value = `Hello, ${name}!
Welcome!`;
price = 14.9;
value = `Price ${price.toFixed(2)} €`; // 'Price: 14.90 €'Note that string templates can be of a single line as well as of multiple lines. For f-strings in Python you can pass the format for variables, but you can't call methods of a variable unless they are properties and call getter methods.
Python and now JavaScript has an interesting feature to assign items of sequences into separate variables. For example, we can read the three values of a list into variables a, b, and c with the following syntax:
[a, b, c] = [1, 2, 3]For tuples the parenthesis can be omitted. The following is a very popular way to swap values of two variables in Python:
a = 1
b = 2
a, b = b, a # swap valuesWith the next generation JavaScript this can also be achieved:
a = 1;
b = 2;
[a, b] = [b, a]; // swap valuesIn Python 3.6 if we have an unknown number of items in a list or tuple, we can assign them to a tuple of several values while also unpacking the rest to a list:
first, second, *the_rest = [1, 2, 3, 4]
# first == 1
# second == 2
# the_rest == [3, 4]This can also be done with JavaScript (ECMAScript 6):
[first, second, ...the_rest] = [1, 2, 3, 4];
// first === 1
// last === 2
// the_rest === [3, 4]Python and JavaScript have a very neat functionality to create functions in a single line. These functions are called lambdas. Lambdas are very simple functions that take one or more arguments and return some calculated value. Usually lambdas are used when you need to pass a function to another function as a callback or as a function to manipulate every separate elements in a sequence.
In Python, you would define a lambda using the lambda keyword, like this:
sum = lambda x, y: x + y
square = lambda x: x ** 2In JavaScript lambdas use the => notation. If there are more than one arguments, they have to be in parenthesis:
sum = (x, y) => x + y;
square = x => Math.pow(x, 2);Many programming languages allow iterating through a sequence only by using indexes and incrementing their values. Then to get an item at some position, you would read it from an array, for example:
for (i=0; i<items.length; i++) {
console.log(items[i]);
}This is not a nice syntax and is very technical - it doesn't read naturally. What we really want is just to grab each value from the list. And Python has a very neat possibility just to iterate through the elements:
for item in ['A', 'B', 'C']:
print(item)In the modern JavaScript this is also possible with the for..of operator:
for (let item of ['A', 'B', 'C']) {
console.log(item);
}You can also iterate through a string characters in Python:
for character in 'ABC':
print(character)And in the modern JavaScript:
for (let character of 'ABC') {
console.log(character);
}Python and modern JavaScript has a possibility to define special functions through which you can iterate. With each iteration they return the next generated value in a sequence. These functions are called generators. With generators you can get numbers in a range, lines from a file, data from different paginated API calls, fibonacci numbers, and any other dynamicly generated sequences.
Technically generators are like normal functions, but instead of returning a value, they yield a value. This value will be returned for one iteration. And this generation happens as long as the end of the function is reached.
To illustrate that, the following Python code will create a generator countdown() which will return numbers from the given one back to 1, (like 10, 9, 8, ..., 1):
def countdown(counter):
while counter > 0:
yield counter
counter -= 1
for counter in countdown(10):
print(counter)Exactly the same can be achieved in modern JavaScript, but notice the asterisk at the function statement. It defines that it's a generator:
function* countdown(counter) {
while (counter > 0) {
yield counter;
counter--;
}
}
for (let counter of countdown(10)) {
console.log(counter);
}We already had a look at lists, tuples and arrays. But here is another type of data - sets. Sets are groups of elements that ensure that each element there exists only once. Set theory also specifies set operations like union, intersection, and difference, but we won't cover them here today.
This is how to create a set, add elements to it, check if a value exists, check the total amount of elements in a set, and iterate through its values, and remove a value using Python:
s = set(['A'])
s.add('B'); s.add('C')
'A' in s
len(s) == 3
for elem in s:
print(elem)
s.remove('C')Here is how to achieve the same in modern JavaScript:
s = new Set(['A']);
s.add('B').add('C');
s.has('A') === true;
s.size === 3;
for (let elem of s.values()) {
console.log(elem);
}
s.delete('C')As you know from the previous parts, I am offering a cheat sheet with the whole list of equivalents in Python and JavaScript, both, time honored and future proof. To have something printed in front of your eyes is much more convenient than switching among windows or scrolling up and down until you find what you exactly were searching for. So I suggest you to get the cheat sheet and use it for good!
In the next and last part of the series, we will have a look at function arguments, classes, inheritance, and properties. Stay tuned!
Cover photo by Alex Knight
Last time we started a new series of articles about analogies in Python and JavaScript. We had a look at lists, arrays, dictionaries, objects, and strings, conditional assignments, and parsing integers. This time we will go through more interesting and more complex things like serializing dictionaries and lists to JSON, operations with regular expressions, as well as raising and catching errors.
When working with APIs it is very usual to serialize objects to JSON format and be able to parse JSON strings.
In Python it is done with the json module like this:
import json
json_data = json.dumps(dictionary, indent=4)
dictionary = json.loads(json_data)Here we'll indent the nested elements in the JSON string by 4 spaces.
In JavaScript there is a JSON object that has methods to create and parse JSON strings:
json_data = JSON.stringify(dictionary, null, 4);
dictionary = JSON.parse(json_data);Regular expressions are multi-tool that once you master, you can accomplish lots of things.
In the last article, we saw how one can join lists of strings into a single string. But how can you split a long string into lists of strings? What if the delimiter can be not a single character as the comma, but a range of possible variations? This can be done with regular expressions and the split() method.
In Python, the split() method belongs to the regular expression pattern object. This is how you could split a text string into sentences by punctuation marks:
import re
# One or more characters of "!?." followed by whitespace
delimiter = re.compile(r'[!?\.]+\s*')
text = "Hello!!! What's new? Follow me."
sentences = delimiter.split(text)
# sentences == ['Hello', "What's new", 'Follow me', '']In JavaScript the split() method belongs to the string:
// One or more characters of "!?." followed by whitespace
delimiter = /[!?\.]+\s*/;
text = "Hello!!! What's new? Follow me.";
sentences = text.split(delimiter)
// sentences === ["Hello", "What's new", "Follow me", ""]Regular expressions are often used to validate data from the forms.
For example, to validate if the entered email address is correct, you would need to match it against a regular expression pattern. In Python that would look like this:
import re
# name, "@", and domain
pattern = re.compile(r'([\w.+\-]+)@([\w\-]+\.[\w\-.]+)')
match = pattern.match('hi@example.com')
# match.group(0) == 'hi@example.com'
# match.group(1) == 'hi'
# match.group(2) == 'example.com'If the text matches the pattern, it returns a match object with the group() method to read the whole matched string, or separate captures of the pattern that were defined with the parenthesis. 0 means getting the whole string, 1 means getting the match in the first group, 2 means getting the match in the second group, and so on. If the text doesn't match the pattern, the None value will be returned.
In JavaScript the match() method belongs to the string and it returns either a match object, or null. Pretty similar:
// name, "@", and domain
pattern = /([\w.+\-]+)@([\w\-]+\.[\w\-.]+)/;
match = 'hi@example.com'.match(pattern);
// match[0] === 'hi@example.com'
// match[1] === 'hi'
// match[2] === 'example.com'The match object in JavaScript acts as an array. Its value at the zeroth position is the whole matched string. The other indexes correspond to the captures of the pattern defined with the parenthesis.
Moreover, sometimes you need to search if a specific value exists in a string and at which letter position it will be found. That can be done with the search() method.
In Python this method belongs to the regular expression pattern and it returns the match object. The match object has the start() method telling at which letter position the match starts:
text = 'Say hi at hi@example.com'
first_match = pattern.search(text)
if first_match:
start = first_match.start() # start == 10In JavaScript the search() method belongs to the string and it returns just an integer telling at which letter position the match starts. If nothing is found, -1 is returned:
text = 'Say hi at hi@example.com';
first_match = text.search(pattern);
if (first_match > -1) {
start = first_match; // start === 10
}
Replacing with regular expressions usually happen when cleaning up data, or adding additional features. For example, we could take some text and make all email addresses clickable.
Python developers would use the sub() method of the regular expression pattern:
html = pattern.sub(
r'<a href="mailto:\g<0>">\g<0></a>',
'Say hi at hi@example.com',
)
# html == 'Say hi at <a href="mailto:hi@example.com">hi@example.com</a>'JavaScript developers would use the replace() method of the string:
html = 'Say hi at hi@example.com'.replace(
pattern,
'<a href="mailto:$&">$&</a>',
);
// html === 'Say hi at <a href="mailto:hi@example.com">hi@example.com</a>'In Python the captures, also called as "backreferences", are accessible in the replacement string as \g<0>, \g<1>, \g<2>, etc. In JavaScript the same is accessible as $&, $1, $2, etc. Backreferences are usually used to wrap some strings or to switch places of different pieces of text.
It is also possible to replace a match with a function call. This can be used to do replacements within replacements or to count or collect some features of a text. For example, using replacements with function calls in JavaScript, I once wrote a fully functional HTML syntax highlighter.
Here let's change all email addresses in a text to UPPERCASE.
In Python, the replacement function receives the match object. We can use its group() method to do something with the matched text and return a text as a replacement:
text = pattern.sub(
lambda match: match.group(0).upper(),
'Say hi at hi@example.com',
)
# text == 'Say hi at HI@EXAMPLE.COM'In JavaScript the replacement function receives the whole match string, the first capture, the second capture, etc. We can do what we need with those values and then return some string as a replacement:
text = 'Say hi at hi@example.com'.replace(
pattern,
function(match, p1, p2) {
return match.toUpperCase();
}
);
// text === 'Say hi at HI@EXAMPLE.COM'Contrary to Python, client-side JavaScript normally isn't used for saving or reading files or connecting to remote databases. So try..catch blocks are quite rare in JavaScript compared to try..except analogy in Python.
Anyway, error handling can be used with custom user errors implemented and raised in JavaScript libraries and caught in the main code.
The following example in Python shows how to define a custom exception class MyException, how to raise it in a function, and how to catch it and handle in a try..except..finally block:
class MyException(Exception):
def __init__(self, message):
self.message = message
def __str__(self):
return self.message
def proceed():
raise MyException('Error happened!')
try:
proceed()
except MyException as err:
print('Sorry! {}'.format(err))
finally:
print('Finishing') The following example in JavaScript does exactly the same: here we define a MyException class, throw it in a function, and catch it and handle in the try..catch..finally block.
function MyException(message) {
this.message = message;
this.toString = function() {
return this.message;
}
}
function proceed() {
throw new MyException('Error happened!');
}
try {
proceed();
} catch (err) {
if (err instanceof MyException) {
console.log('Sorry! ' + err);
}
} finally {
console.log('Finishing');
}
The MyException class in both languages has a parameter message and a method to represent itself as a string using the value of the message.
Of course, exceptions should be raised/thrown just in the case of errors. And you define what is an error in your module design.
As I mentioned last time, you can grab a side-by-side comparison of Python and JavaScript that I compiled for you (and my future self). Side by side you will see features from traditional list, array, dictionary, object, and string handling to modern string interpolation, lambdas, generators, sets, classes, and everything else. Use it for good.
In the next part of the series, we will have a look at textual templates, list unpacking, lambda functions, iteration without indexes, generators, and sets. Stay tuned!
Cover photo by Benjamin Hung.
Although Python and JavaScript are quite different languages, there are some analogies which full stack Python developers should know when developing web projects. In this series of 4 parts, I will explore what is similar in each of those languages and what are the common ways to solve common problems. This is not meant to be a reference and I will skip the basics like primitive variable types, conditions, and loops. But I will dig into more complex structures and data operations using both, Python and JavaScript. Also, I will try to focus on the practical use cases. This series should be interesting for the developers of Django, Flask, or another Python framework who want to get a grasp of traditional and modern vanilla JavaScript. On the other hand, it will be useful for the front-enders who want to better understand how the backend is working and maybe even start their own Django website.
We'll begin with integer parsing.
In Python that's straightforward:
number = int(text)But in JavaScript you have to explain what number system you expect: decimal, octal, hexadecimal, or binary:
number = parseInt(text, 10);To use the "normal" decimal number system we are passing number 10 as the second parameter of the parseInt() function. 8 goes for octal, 16 for hexadecimal, or 2 – for binary. If the second parameter is missing, the number in text starts with zero, and you are using a slightly older browser, the number in the text will be interpreted as octal. For example,
parseInt('012') == 10 // in some older browsers
parseInt('012', 10) == 12And that can really mess up your calculations.
For conditional assignment, Python and JavaScript have different syntaxes, but conditional assignments are quite popular in both languages. That's popular, because it's just a single statement to have a condition checking, the true-case value, and the false-case value.
Since Python 2.7 you can write conditional assignments like this:
value = 'ADULT' if age >= 18 else 'CHILD'In JavaScript conditional assignments are done using ternary operator ?:, similar to the ones in C, C++, C#, Java, Ruby, PHP, Perl, Swift, and ActionScript:
value = age >= 18? 'ADULT': 'CHILD';The normal way to access an object's attribute is by the dot notation in both, Python and JavaScript:
obj.color = 'YELLOW'But what if you want to refer to an attribute by its name saved as a string? For example, the attribute name could be coming from a list of attributes or the attribute name is combined from two strings like 'title_' + lang_code.
For that reason, in Python, there are functions getattr() and setattr(). I use them a lot.
attribute = 'color'
value = getattr(obj, attribute, 'GREEN')
setattr(obj, attribute, value)In JavaScript you can treat an object like a dictionary and pass the attribute name in square brackets:
attribute = 'color';
value = obj[attribute] || 'GREEN';
obj[attribute] = value;To retrieve a default value when an object has no such attribute, in Python, getattr() has the third parameter. In JavaScript, if obj attribute doesn't exist, it will return the undefined value. Then it can be OR-ed with the default value that you want to assign. That's a common practice in JavaScript that you can find in many JavaScript libraries and frameworks.
This is similar to the previous one. The normal way to assign a dictionary's value by key in both languages is using the square brackets:
dictionary = {}
dictionary['color'] = 'YELLOW'To read a value in Python you can use the square-bracket notation, but it will fail on non-existing keys with KeyError. The more flexible way is to use the get() method which returns None for non-existing keys. Also you can pass an optional default value as the second parameter:
key = 'color'
value = dictionary.get(key, 'GREEN')In JavaScript you would use the same trick as with object attributes, because dictionaries and objects are the same there:
key = 'color';
value = dictionary[key] || 'GREEN';Python has the slice [:] operator to get parts of lists, tuples, and similar more complex structures, for example Django QuerySets:
items = [1, 2, 3, 4, 5]
first_two = items[:2] # [1, 2]
last_two = items[-2:] # [4, 5]
middle_three = items[1:4] # [2, 3, 4]In JavaScript arrays have the slice() method with the same effect and similar usage:
items = [1, 2, 3, 4, 5];
first_two = items.slice(0, 2); // [1, 2]
last_two = items.slice(-2); // [4, 5]
middle_three = items.slice(1, 4); // [2, 3, 4]But don't mix it up with the splice() method which modifies the original array!
The [:] slice operator in Python also works for strings:
text = 'ABCDE'
first_two = text[:2] # 'AB'
last_two = text[-2:] # 'DE'
middle_three = text[1:4] # 'BCD'In JavaScript strings just like arrays have the slice() method:
text = 'ABCDE';
first_two = text.slice(0, 2); // 'AB'
last_two = text.slice(-2); // 'DE'
middle_three = text.slice(1, 4); // 'BCD'In programming it is very common to collect and analyze sequences of elements. In Python that is usually done with lists and in JavaScript with arrays. They have similar syntax and operations, but different method names to add and remove values.
This is how to concatenate two lists, add one value to the end, add one value to the beginning, get and remove a value from the beginning, get and remove a value from the end, and delete a certain value by index in Python:
items1 = ['A']
items2 = ['B']
items = items1 + items2 # items == ['A', 'B']
items.append('C') # ['A', 'B', 'C']
items.insert(0, 'D') # ['D', 'A', 'B', 'C']
first = items.pop(0) # ['A', 'B', 'C']
last = items.pop() # ['A', 'B']
items.delete(0) # ['B']This is how to do exactly the same with arrays in JavaScript:
items1 = ['A'];
items2 = ['B'];
items = items1.concat(items2); // items === ['A', 'B']
items.push('C'); // ['A', 'B', 'C']
items.unshift('D'); // ['D', 'A', 'B', 'C']
first = items.shift(); // ['A', 'B', 'C']
last = items.pop(); // ['A', 'B']
items.splice(0, 1); // ['B']It is very common after having a list or array of strings, to combine them into one string by a separator like comma or new line.
In Python that is done by the join() method of a string where you pass the list or tuple. Although it might feel unnatural, you start with the separator there. But I can assure that you get used to it after several times of usage.
items = ['A', 'B', 'C']
text = ', '.join(items) # 'A, B, C'In JavaScript the array has the join() method where you pass the separator:
items = ['A', 'B', 'C'];
text = items.join(', '); // 'A, B, C'I compiled the whole list of equivalents of Python and JavaScript to a cheat sheet that you can print out and use for good. Side by side it compares traditional Python 2.7 and JavaScript based on ECMAScript 5 standard, as well as newer Python 3.6 and JavaScript based on ECMAScript 6 standard with such goodies as string interpolation, lambdas, generators, classes, etc.
In the next part of the series, we will have a look at JSON creation and parsing, operations with regular expressions, and error handling. Stay tuned!
Cover photo by Benjamin Hung.
Highcharts is, in my opinion, one of the best JavaScript libraries to work with data visualization and charts out there. Even though Highcharts is open source, it’s a commercial library. It’s free for use in non-commercial applications though.
In this tutorial we are going to explore how to integrate it with a Django project to render dynamically generated charts. In relation to drawing the charts and rendering it to the client, all the hard work is done by Highcharts at the client side. The configuration and setup is pure JavaScript.
The main challenge here is on how to translate the data from your backend to a format that Highcharts will understand. This data may come from a database or an external API, and probably is represented as Python objects (like in a QuerySet), or simply be represented as Python dictionaries or lists.
Generally speaking, there are two ways to do it:
The first option is like a brute force and in many cases the easiest way. The second option requires a slightly complicated setup, but you will also benefit from the page loading speed and from the maintainability of the code.
Basically we just need to include the Highcharts library in our template and we are ready to go. You can either download and serve it locally or simply use their CDN:
<script src="https://code.highcharts.com/highcharts.src.js"></script>Now we need some data. I thought that it would be fun to play with an existing dataset. The Titanic dataset is pretty famous one, and easy to access.
What I did here was loading the dataset (1300~ rows) into a model named Passenger:
class Passenger(models.Model):
name = models.CharField()
sex = models.CharField()
survived = models.BooleanField()
age = models.FloatField()
ticket_class = models.PositiveSmallIntegerField()
embarked = models.CharField()If you are familiar with data mining, data science, or machine learning probably you already know this data set. This dataset is usually used for learning purpose. It’s composed by the list of passengers of the famous RMS Titanic tragedy.
We won’t be doing anything smart with it, just querying the database and displaying the data using Highcharts.
I won’t dive into deep details about Highcharts. The goal is to understand how to make Django and Highcharts talk. For details about how to do this or that with Highcharts, best thing is to consult the official documentation.
Here is a working example of a column chart using Highcharts:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Django Highcharts Example</title>
</head>
<body>
<div id="container"></div>
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script>
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Historic World Population by Region'
},
xAxis: {
categories: ['Africa', 'America', 'Asia', 'Europe', 'Oceania']
},
series: [{
name: 'Year 1800',
data: [107, 31, 635, 203, 2]
}, {
name: 'Year 1900',
data: [133, 156, 947, 408, 6]
}, {
name: 'Year 2012',
data: [1052, 954, 4250, 740, 38]
}]
});
</script>
</body>
</html>The code above generates the following chart:
![]()
The basic structure here is:
Highcharts.chart('id_of_the_container', {
// dictionary of options/configuration
});The most straightforward way to do it is by writing directly in the template (which is not recommended).
Let’s write an query to display the number of survivors and deaths organized by ticket class.
views.py
from django.db.models import Count, Q
from django.shortcuts import render
from .models import Passenger
def ticket_class_view(request):
dataset = Passenger.objects \
.values('ticket_class') \
.annotate(survived_count=Count('ticket_class', filter=Q(survived=True)),
not_survived_count=Count('ticket_class', filter=Q(survived=False))) \
.order_by('ticket_class')
return render(request, 'ticket_class.html', {'dataset': dataset})The queryset above generates a data in the following format:
[
{'ticket_class': 1, 'survived_count': 200, 'not_survived_count': 123},
{'ticket_class': 2, 'survived_count': 119, 'not_survived_count': 158},
{'ticket_class': 3, 'survived_count': 181, 'not_survived_count': 528}
]Then we could just write it in the template, inside the JavaScript tags:
ticket_class.html
<div id="container"></div>
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script>
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Titanic Survivors by Ticket Class'
},
xAxis: {
categories: [
{% for entry in dataset %}'{{ entry.ticket_class }} Class'{% if not forloop.last %}, {% endif %}{% endfor %}
]
},
series: [{
name: 'Survived',
data: [
{% for entry in dataset %}{{ entry.survived_count }}{% if not forloop.last %}, {% endif %}{% endfor %}
],
color: 'green'
}, {
name: 'Not survived',
data: [
{% for entry in dataset %}{{ entry.not_survived_count }}{% if not forloop.last %}, {% endif %}{% endfor %}
],
color: 'red'
}]
});
</script>![]()
This kind of strategy is not really a good idea because the code is hard to read, hard to maintain and it is too easy
to shoot in the foot. Because we are using Python to generate JavaScript code, we have to format it properly.
For example, the code {% if not forloop.last %}, {% endif %} is to not append a comma (,) after
the last item of the array (otherwise the result would be [200, 119, 181,]). The newest JavaScript versions are
forgiving and accepts an extra comma (like Python does), but older versions don’t, so it might cause problem in old
browsers. Anyway, the point is you have to make sure your Python code is writing valid JavaScript code.
A slightly better way to do it would be processing the data a little bit more in the view:
views.py
import json
from django.db.models import Count, Q
from django.shortcuts import render
from .models import Passenger
def ticket_class_view_2(request):
dataset = Passenger.objects \
.values('ticket_class') \
.annotate(survived_count=Count('ticket_class', filter=Q(survived=True)),
not_survived_count=Count('ticket_class', filter=Q(survived=False))) \
.order_by('ticket_class')
categories = list()
survived_series = list()
not_survived_series = list()
for entry in dataset:
categories.append('%s Class' % entry['ticket_class'])
survived_series.append(entry['survived_count'])
not_survived_series.append(entry['not_survived_count'])
return render(request, 'ticket_class_2.html', {
'categories': json.dumps(categories),
'survived_series': json.dumps(survived_series),
'not_survived_series': json.dumps(not_survived_series)
})ticket_class_2.html
<div id="container"></div>
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script>
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Titanic Survivors by Ticket Class'
},
xAxis: {
categories: {{ categories|safe }}
},
series: [{
name: 'Survived',
data: {{ survived_series }},
color: 'green'
}, {
name: 'Not survived',
data: {{ not_survived_series }},
color: 'red'
}]
});
</script>Here’s what we are doing: first run through the queryset and create three separate lists, append the values and do the
formatting. After that, use the json module and dump the Python lists into JSON format. The result are Python strings
properly formatted as JSON data.
We have to use the safe template filter to properly render the categories because Django automatically escape
characters like ' and " for safety reason, so we have to instruct Django to trust and render it as it is.
We could also do all the configuration in the backend, like this:
views.py
import json
from django.db.models import Count, Q
from django.shortcuts import render
from .models import Passenger
def ticket_class_view_3(request):
dataset = Passenger.objects \
.values('ticket_class') \
.annotate(survived_count=Count('ticket_class', filter=Q(survived=True)),
not_survived_count=Count('ticket_class', filter=Q(survived=False))) \
.order_by('ticket_class')
categories = list()
survived_series_data = list()
not_survived_series_data = list()
for entry in dataset:
categories.append('%s Class' % entry['ticket_class'])
survived_series_data.append(entry['survived_count'])
not_survived_series_data.append(entry['not_survived_count'])
survived_series = {
'name': 'Survived',
'data': survived_series_data,
'color': 'green'
}
not_survived_series = {
'name': 'Survived',
'data': not_survived_series_data,
'color': 'red'
}
chart = {
'chart': {'type': 'column'},
'title': {'text': 'Titanic Survivors by Ticket Class'},
'xAxis': {'categories': categories},
'series': [survived_series, not_survived_series]
}
dump = json.dumps(chart)
return render(request, 'ticket_class_3.html', {'chart': dump})ticket_class_3.html
<div id="container"></div>
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script>
Highcharts.chart('container', {{ chart|safe }});
</script>As you can see, that way we move all the configuration to the server side. But we are still interacting with the JavaScript code directly.
Now this is how I usually like to work with Highcharts (or any other JavaScript library that interacts with the server).
The idea here is to render the chart using an asynchronous call, returning a JsonResponse from the server.
This time, we are going to need two routes:
urls.py
from django.urls import path
from passengers import views
urlpatterns = [
path('json-example/', views.json_example, name='json_example'),
path('json-example/data/', views.chart_data, name='chart_data'),
]The json_example URL route is pointing to a regular view, which will render the template which will invoke the
chart_data view. This call can be automatic upon page load, or it can be triggered by an action (a button click
for example).
views.py
def json_example(request):
return render(request, 'json_example.html')
def chart_data(request):
dataset = Passenger.objects \
.values('embarked') \
.exclude(embarked='') \
.annotate(total=Count('embarked')) \
.order_by('embarked')
port_display_name = dict()
for port_tuple in Passenger.PORT_CHOICES:
port_display_name[port_tuple[0]] = port_tuple[1]
chart = {
'chart': {'type': 'pie'},
'title': {'text': 'Titanic Survivors by Ticket Class'},
'series': [{
'name': 'Embarkation Port',
'data': list(map(lambda row: {'name': port_display_name[row['embarked']], 'y': row['total']}, dataset))
}]
}
return JsonResponse(chart)Here we can see the json_example is nothing special, just returning the json_example.html template, which we are
going to explore in a minute.
The chart_data is the one doing all the hard work. Here we have the database query and building the chart
dictionary. In the end we return the chart data as a JSON object.
json_example.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Django Highcharts Example</title>
</head>
<body>
<div id="container" data-url="{% url 'chart_data' %}"></div>
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$.ajax({
url: $("#container").attr("data-url"),
dataType: 'json',
success: function (data) {
Highcharts.chart("container", data);
}
});
</script>
</body>
</html>Here is where the magic happens. The div with id container is where the chart is going to be rendered. Now, observe
that I included a custom attribute named data-url. Inside this attribute I stored the path to the view that will be
used to load the chart data.
Inside the ajax call, we make the request based on the URL provided in the data-url and instruct the ajax request
that we are expecting a JSON object in return (defined by the dataType). When the request completes, the JSON
response will be inside the data parameter in the success function. Finally, inside the success function, we
render the chart using the Highcharts API.
This is extremely useful because now we can decouple all the JavaScript from our template. In this example we used a single file for simplicity, but nothing stops us now from saving the script tag content in a separate file. This is great because we are no longer mixing the Django template language with JavaScript.
The result is the following screen shot:
![]()
In this tutorial we explored the basics on how to integrate Highcharts.js with Django. The implementation concepts used in this tutorial can be applied in other charts libraries such as Charts.js. The process should be very similar.
Whenever possible, try to avoid interacting with JavaScript code using the Django Template Language. Prefer returning the data as JSON objects already processed and ready to use.
Usually when working with charts and data visualization the most challenging part is to squeeze the data in the format required to render the chart. What I usually do is first create a static example hardcoding the data so I can have an idea about the data format. Next, I start creating the QuerySet using in the Python terminal. After I get it right, I finally write the view function.
If you want to learn more, the best way is to get your hands dirty. Here’s something you can do: