From JavaScript to Python: A Comprehensive Cheat Sheet and Hands-On Guide

Published by

on

From JavaScript to Python

Moving from Node.js to Python with ArtBloom’s Project


Introduction

💻 Dive into the full source code and docs for ArtBloom on GitHub 🚀

The choice of a backend technology often determines the scalability, maintainability, and performance of a project. While Node.js dominates the backend ecosystem with its event-driven architecture, Python offers a compelling alternative with its readability, rich libraries, and async capabilities. This article provides a practical roadmap for developers transitioning from Node.js to Python by diving into ArtBloom—a scalable art discovery backend built with Sanic and Tortoise ORM. From modular project structure to an intelligent recommendation system powered by Pandas, you’ll learn how Python unlocks efficiency and elegance in backend development.

ArtBloom: Project Overview

Why Python for Backend Development?

Backend development demands a balance between simplicity, scalability, and performance. Python excels in these areas, offering a clean syntax that minimizes boilerplate code while remaining highly expressive. With frameworks like Sanic for asynchronous tasks and Tortoise ORM for seamless database management, Python empowers developers to build scalable APIs effortlessly. Its extensive ecosystem includes tools like Pandas for data manipulation and HTTPx for efficient API integration, making Python a versatile choice for both traditional and data-driven applications. In ArtBloom, Python’s strengths shine through in its modular design, efficient asynchronous processing, and robust recommendation engine.

Learning Objectives

This guide bridges the gap between theory and practice, enabling developers to harness Python’s backend capabilities effectively. By working through the ArtBloom project, you will:

  • Master Python’s asynchronous programming model with Sanic.
  • Implement modular architectures using Python’s package system.
  • Interact with databases seamlessly using Tortoise ORM.
  • Develop RESTful APIs with efficient routing and error handling.
  • Build and optimize a recommendation system using Pandas for real-time data analysis.
  • Adopt best practices for testing, linting, and deployment in production-grade Python projects.

These objectives ensure you leave equipped to design scalable, maintainable backend systems with Python.

Technology Stack

ArtBloom leverages a modern Python stack designed for performance, scalability, and developer productivity:

  • Sanic: An asynchronous web framework that enables high-performance, non-blocking APIs.
  • Tortoise ORM: A Pythonic, asynchronous ORM for seamless database interaction.
  • Aerich: A lightweight migration tool to manage database schema evolution.
  • HTTPx: A robust library for making HTTP requests asynchronously.
  • Python-dotenv: Simplifies environment variable management for secure configurations.
  • Pandas: Empowers advanced data analysis and manipulation for features like recommendations.
  • PyUnit: A comprehensive testing framework to ensure code reliability.
  • Pylint & isort: Tools for maintaining code quality and consistency.
  • PostgreSQL: A powerful, open-source database optimized for scalability and complex queries.

This stack reflects the synergy between Python’s async ecosystem and real-world backend demands.

Project Structure

ArtBloom’s project structure is designed for clarity, scalability, and maintainability. It organizes code into modular components, enabling seamless development and testing:

art-bloom/
├── artworks_core/             # Core application logic
│   ├── models/                # Database models
│   │   ├── __init__.py
│   │   ├── artwork.py
│   ├── artworks_router.py     # API route definitions
│   ├── data_processor.py      # Data processing utilities
├── artworks_settings/         # Configuration and environment management
│   ├── setup_env_configuration.py
│   ├── generate_tortoise_config.py
│   └── tortoise_config_wrapper.py
├── artworks_utils/            # Shared utilities
│   ├── app_logger.py          # Centralized logging
│   ├── database_manager.py    # Database connection logic
│   ├── http_request_manager.py# HTTP requests handling
├── tests/                     # Unit and integration tests
│   ├── test_format_artworks.py
├── migrations/                # Database migration files
├── server.py                  # Application entry point
├── Makefile                   # Build and management commands
├── Pipfile                    # Dependency management
├── README.md                  # Project documentation

This structure reflects Python’s philosophy of simplicity and readability while supporting production-level backend development.

Setup and Installation

To set up ArtBloom locally, follow the detailed instructions in the Setup and Installation section of the GitHub repository. The guide covers:

  • Cloning the repository.
  • Installing dependencies using Pipenv.
  • Configuring environment variables for secure setup.
  • Initializing the database with Aerich.
  • Running the application on your local environment.

This ensures you have everything configured and ready to start exploring or contributing to ArtBloom.

ArtBloom API Endpoints

ArtBloom provides a robust set of RESTful API endpoints to interact with artwork data, designed for performance and scalability. For a detailed list and usage examples, refer to the API Endpoints section in the GitHub repository.

Recommendation System

The recommendation system in ArtBloom is designed to provide personalized artwork suggestions based on user preferences and metadata analysis. It combines user-provided preferences, such as artistic style, medium, and categories, with advanced filtering and ranking mechanisms. By incorporating temporal diversity, the system ensures a balanced mix of relevance and variety in its recommendations.

How It Works?

  1. User Preferences
    The system begins by collecting user preferences, such as desired artistic style, medium, and preferred categories.
  2. Filtering and Scoring
    Artworks are filtered based on these preferences, and a scoring system ranks them according to their relevance.
  3. Temporal Diversity
    To provide a diverse selection, the system analyzes creation dates and balances recommendations across time periods.
  4. Ranking and Selection
    Artworks are ranked by their scores and temporal similarity, ensuring the most relevant and diverse options are presented to the user.

For detailed technical implementation, including examples and code, please refer to the Recommendation System section in the GitHub repository.


Transitioning from Node.js to Python Guide

General Comparison: Python vs JavaScript

ConceptJavaScriptPython
Variableslet, constx = 10 (dynamic typing)
Arrow Functions (Standalone)const square = (x) => x * x;square = lambda x: x * x
Arrow Functions (Iterator)array.map((x) => x * 2)[x * 2 for x in array]
Importsimport fs from 'fs';import os
Async/Await Syntaxasync function fetchData() {
await callApi();
}
async def fetch_data():
await call_api()
String Interpolation`Hello, ${name}!`f"Hello, {name}!"
Equality Checkif (a === b)if a == b:
For Loopfor (let i = 0; i < 5; i++) {}for i in range(5):
If/Else If/Elseif (x > 90) { ... } else if (x > 75) { ... } else { ... }if x > 90: ... elif x > 75: ... else: ...
Try/Catchtry { ... } catch (err) { ... }try: ... except Exception as e: ...
Boolean Logica && ba and b
List Comprehensionarray.filter(x => x > 2)[x for x in array if x > 2]
Dictionary (Object) Accessobj['key']dict['key']
Dictionary IterationObject.entries(obj).forEach(([k, v]) => {})for k, v in dict.items():
Array Destructuringconst [a, b] = array;a, b = array
Object Destructuringconst {key} = obj;key = obj['key']
Function Argumentsfunction f(a, ...args)def f(a, *args):

Module Imports and Exports

Project Structure

Python
project/
├── utils/
│   ├── __init__.py
│   ├── logger.py
│   ├── http_request_manager.py
├── core/
│   ├── data_processor.py
├── main.py
Node.js
project/
├── utils/
│   ├── index.js
│   ├── logger.js
│   ├── httpRequestManager.js
├── core/
│   ├── dataProcessor.js
├── main.js

Same-Package Imports

Python

utils/http_request_manager.py:

import httpx
from .logger import logger

async def handle_get_request(api_url, params):
    async with httpx.AsyncClient() as client:
        response = await client.get(api_url, params=params)
        logger.info("Request successful")
        return response.json()

main.py:

from utils.http_request_manager import handle_get_request

response = await handle_get_request("https://api.example.com", {"q": "test"})
logger.info(response)
Node.js

utils/httpRequestManager.js:

import fetch from 'node-fetch';
import { logger } from './logger.js';

export const handleGetRequest = async (apiUrl, params) => {
  try {
    const query = new URLSearchParams(params).toString();
    const response = await fetch(`${apiUrl}?${query}`);
    const data = await response.json();
    logger.info("Request successful");
    return data;
  } catch (err) {
    logger.error("Request failed", err);
    throw err;
  }
};

main.js:

import { handleGetRequest } from './utils/httpRequestManager.js';

const response = await handleGetRequest("https://api.example.com", { q: "test" });

logger.info(response);

Cross-Package Import

Python

utils/init.py:

from .logger import logger
from .http_request_manager import handle_get_request

__all__ = ["logger", "handle_get_request"]

core/data_processor.py:

from utils import handle_get_request, logger

async def process_data():
    response = await handle_get_request("https://api.example.com", {"q": "data"})
    logger.info(f"Processed data: {response}")
Node.js

utils/index.js:

export { logger } from './logger.js';
export { handleGetRequest } from './httpRequestManager.js';

core/dataProcessor.js:

import { handleGetRequest, logger } from '../utils/index.js';

export const processData = async () => {
  const response = await handleGetRequest("https://api.example.com", { q: "data" });
  logger.info(`Processed data: ${JSON.stringify(response)}`);
};

Files, Lists, and Dictionaries: Python vs JavaScript

Project Structure

Python
project/
├── utils/
│   ├── file_manager.py
├── main.py
Node.js
project/
├── utils/
│   ├── fileManager.js
├── main.js

Reading and Writing Files

Python

utils/file_manager.py:

def write_to_file(file_path, content):
    with open(file_path, 'w') as file:
        file.write(content)

def read_from_file(file_path):
    with open(file_path, 'r') as file:
        return file.read()

main.py:

from utils.file_manager import write_to_file, read_from_file

file_path = "example.txt"
write_to_file(file_path, "Hello, Python!")
content = read_from_file(file_path)
logger.info(content)  # Add a comment describing the expected output.  # Outputs: content
Node.js

utils/fileManager.js:

import fs from 'fs/promises';

export const writeToFile = async (filePath, content) => {
  await fs.writeFile(filePath, content, 'utf8');
};

export const readFromFile = async (filePath) => {
  return await fs.readFile(filePath, 'utf8');
};

main.js:

import { writeToFile, readFromFile } from './utils/fileManager.js';

const filePath = 'example.txt';
await writeToFile(filePath, 'Hello, JavaScript!');
const content = await readFromFile(filePath);
logger.info(content);

List Operations

Python

main.py:

numbers = [1, 2, 3, 4, 5]

# Add
numbers.append(6)
logger.info(numbers)  # Outputs: [1, 2, 3, 4, 5, 6]

# Remove
numbers.remove(3)
logger.info(numbers)

# Filter
even_numbers = [num for num in numbers if num % 2 == 0]
logger.info(even_numbers)  # Outputs: [2, 4, 6]

# Map
squared_numbers = [num ** 2 for num in numbers]
logger.info(squared_numbers)  # Outputs: [1, 4, 9, 16, 25, 36]
Node.js

main.js:

const numbers = [1, 2, 3, 4, 5];

// Add
numbers.push(6);
logger.info(numbers);

// Remove
numbers.splice(numbers.indexOf(3), 1);
logger.info(numbers);

// Filter
const evenNumbers = numbers.filter(num => num % 2 === 0);
logger.info(evenNumbers);

// Map
const squaredNumbers = numbers.map(num => num ** 2);
logger.info(squaredNumbers);

Dictionary (Object) Operations

Python

main.py:

person = {
    "name": "John",
    "age": 30,
    "city": "New York"
}

# Add
person["profession"] = "Engineer"
logger.info(person)  # Outputs: {'name': 'John', 'age': 30, 'profession': 'Engineer'}

# Remove
person.pop("city")
logger.info(person)

# Iterate
for key, value in person.items():
    logger.info(f"{key}: {value}")  # Outputs: 'name: John', 'age: 30', 'profession: Engineer'
Node.js

main.js:

const person = {
    name: "John",
    age: 30,
    city: "New York"
};

// Add
person.profession = "Engineer";
logger.info(person);

// Remove
delete person.city;
logger.info(person);

// Iterate
for (const [key, value] of Object.entries(person)) {
    logger.info(`${key}: ${value}`);
}

Advanced Features: Python vs JavaScript

Project Structure

Python
project/
├── core/
│   ├── artworks_router.py
│   ├── database_manager.py
├── models/
│   ├── __init__.py
│   ├── artwork.py
├── settings/
│   ├── setup_env_configuration.py
│   ├── generate_tortoise_config.py
├── main.py
Node.js
project/
├── core/
│   ├── artworksRouter.js
│   ├── databaseManager.js
├── models/
│   ├── index.js
│   ├── artwork.js
├── settings/
│   ├── setupEnv.js
│   ├── generateSequelizeConfig.js
├── main.js

Framework Comparison: Sanic vs Express

Python (Sanic)

core/artworks_router.py:

from sanic import Blueprint, json

artworks_router = Blueprint("artworks_router")

@artworks_router.get("/artworks")
async def get_artworks(request):
    return json({"message": "List of artworks"})

@router.post("/artworks")
async def create_artwork(request):
    return json({"message": "Artwork created"}, status=201)

main.py:

from sanic import Sanic
from core.artworks_router import artworks_router

app = Sanic("ArtProject")
app.blueprint(artworks_router)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)
JavaScript (Express)

core/artworksRouter.js:

import express from 'express';

const artworksRouter = express.Router();

router.get('/artworks', (req, res) => {
  res.json({ message: 'List of artworks' });
});

router.post('/artworks', (req, res) => {
  res.status(201).json({ message: 'Artwork created' });
});

export default router;

main.js:

import express from 'express';
import artworksRouter from './core/artworksRouter.js';

const app = express();
app.use(artworksRouter);

app.listen(8000, () => {
  console.log('Server running on http://localhost:8000');
});

ORM Comparison: Tortoise ORM vs Sequelize

Python (Tortoise ORM)

models/artwork.py:

from tortoise.models import Model
from tortoise import fields

class Artwork(Model):
    id = fields.IntField(pk=True)
    title = fields.CharField(max_length=255)
    artist = fields.CharField(max_length=255)
    year = fields.IntField(null=True)

settings/generate_tortoise_config.py:

def get_tortoise_config():
    return {
        "connections": {"default": "sqlite://db.sqlite3"},
        "apps": {
            "models": {
                "models": ["models.artwork", "aerich.models"],
                "default_connection": "default",
            },
        },
    }

core/database_manager.py:

from tortoise import Tortoise
from settings.generate_tortoise_config import get_tortoise_config

async def init_database():
    await Tortoise.init(config=get_tortoise_config())
    await Tortoise.generate_schemas()
JavaScript (Sequelize)

models/artwork.js:

import { DataTypes } from 'sequelize';
import sequelize from '../settings/generateSequelizeConfig.js';

const Artwork = sequelize.define('Artwork', {
  id: {
    type: DataTypes.INTEGER,
    autoIncrement: true,
    primaryKey: true
  },
  title: {
    type: DataTypes.STRING,
    allowNull: false
  },
  artist: {
    type: DataTypes.STRING,
    allowNull: false
  },
  year: {
    type: DataTypes.INTEGER,
    allowNull: true
  }
});

export default Artwork;

settings/generateSequelizeConfig.js:

import { Sequelize } from 'sequelize';

const sequelize = new Sequelize('sqlite::memory:');

export default sequelize;

core/databaseManager.js:

import sequelize from '../settings/generateSequelizeConfig.js';

export const initDatabase = async () => {
  try {
    await sequelize.authenticate();
    await sequelize.sync();
    console.log('Database initialized');
  } catch (err) {
    console.error('Database initialization failed', err);
  }
};

Middleware and Configuration

Python

settings/setup_env_configuration.py:

import os
from dotenv import load_dotenv

load_dotenv()

def get_config():
    return {
        "APP_NAME": os.getenv("APP_NAME", "ArtProject"),
        "DEBUG": os.getenv("DEBUG", "False").lower() in ("true", "1"),
        "DATABASE_URL": os.getenv("DATABASE_URL"),
    }
JavaScript

settings/setupEnv.js:

import dotenv from 'dotenv';
dotenv.config();

export const getConfig = () => ({
  appName: process.env.APP_NAME || 'ArtProject',
  debug: process.env.DEBUG === 'true',
  databaseUrl: process.env.DATABASE_URL
});

NumPy and Pandas: Essential Python Methods

Creating Arrays

import numpy as np

# Create arrays
scores = np.array([1, 2, 3, 4, 5])
zero_matrix = np.zeros((2, 3))  # 2x3 matrix of zeros
one_matrix = np.ones((3, 3))    # 3x3 matrix of ones

logger.info(f"Scores array: {scores}")  # Outputs: [1 2 3 4 5]
logger.info(f"Zero matrix: {zero_matrix}")  # Outputs: [[0. 0. 0.]
                                            #           [0. 0. 0.]]

Slicing and Indexing

import numpy as np

# Slicing
scores = np.array([10, 20, 30, 40, 50])
subset_scores = scores[1:4]  # [20, 30, 40]
logger.info(f"Subset of scores: {subset_scores}")

Mathematical Operations

import numpy as np

# Element-wise operations
numbers = np.array([1, 2, 3, 4, 5])
squared_numbers = numbers ** 2
logger.info(f"Squared numbers: {squared_numbers}")  # Outputs: [1 4 9 16 25]

# Aggregates
total = numbers.sum()
average = numbers.mean()
logger.info(f"Sum: {total}, Mean: {average}")  # Outputs: Sum: 15, Mean: 3.0

Creating DataFrames

import pandas as pd

# Create a DataFrame
student_data = {
    'Name': ['Alice', 'Bob', 'Charlie'],
    'Age': [25, 30, 35],
    'City': ['New York', 'Los Angeles', 'Chicago']
}
students_df = pd.DataFrame(student_data)
logger.info(f"Student DataFrame: {students_df}")

Filtering Rows

# Filter rows
filtered_students = students_df[students_df['Age'] > 30]
logger.info(f"Filtered students: {filtered_students}")

Grouping and Aggregation

# Group by and aggregate
grouped_by_city = students_df.groupby('City').agg({'Age': 'mean'})
logger.info(f"Grouped by city: {grouped_by_city}")

Merging DataFrames

import pandas as pd

# Merge DataFrames
left_df = pd.DataFrame({'ID': [1, 2], 'Name': ['Alice', 'Bob']})
right_df = pd.DataFrame({'ID': [1, 2], 'Score': [85, 90]})
merged_df = pd.merge(left_df, right_df, on='ID')
logger.info(f"Merged DataFrame: {merged_df}")

Pivot Tables

# Create pivot table
pivot_table = students_df.pivot_table(values='Age', index='City', columns='Name')
logger.info(f"Pivot Table: {pivot_table}")

ArtBloom Use Case Examples

Data Cleaning and Transformation

# Sample data from ArtBloom
artworks_data = [
    {'id': 1, 'title': 'Starry Night', 'artist': 'Van Gogh', 'year': 1889},
    {'id': 2, 'title': 'Mona Lisa', 'artist': 'Da Vinci', 'year': 1503},
    {'id': 3, 'title': 'The Scream', 'artist': 'Munch', 'year': 1893}
]

# Convert to DataFrame
artworks_df = pd.DataFrame(artworks_data)
logger.info(f"Artworks DataFrame: {artworks_df}")

# Filter artworks after 1800
modern_artworks = artworks_df[artworks_df['year'] > 1800]
logger.info(f"Modern Artworks: {modern_artworks}")

Recommendations Based on User Preferences

# Example user preferences
user_preferences = {
    'style': 'Impressionism',
    'year_range': (1800, 1900)
}

# Sample artworks DataFrame
artworks_df = pd.DataFrame([
    {'title': 'Starry Night', 'style': 'Post-Impressionism', 'year': 1889},
    {'title': 'Water Lilies', 'style': 'Impressionism', 'year': 1906},
    {'title': 'The Scream', 'style': 'Expressionism', 'year': 1893}
])

# Filter based on preferences
recommended_artworks = artworks_df[
    (artworks_df['style'] == user_preferences['style']) &
    (artworks_df['year'] >= user_preferences['year_range'][0]) &
    (artworks_df['year'] <= user_preferences['year_range'][1])
]

logger.info(f"Recommended Artworks: {recommended_artworks}")  # Outputs: Recommended Artworks: [{'title': 'Water Lilies', 'style': 'Impressionism', 'year': 1906}]

Conclusion

Transitioning from Node.js to Python through the ArtBloom project offers a unique opportunity to explore the strengths of Python in backend development. By leveraging Python’s clean syntax, robust libraries, and async capabilities, ArtBloom exemplifies how to build scalable, maintainable, and feature-rich backend systems.

ArtBloom is not just a practical guide but a demonstration of Python’s versatility in real-world applications. From crafting modular architectures with Sanic to enabling advanced recommendations with Pandas, this project illustrates how Python can simplify complex backend workflows while maintaining high performance.

For developers familiar with Node.js, this transition opens up a world of new possibilities. Python’s ecosystem, combined with its simplicity, provides a natural and efficient way to manage databases, develop APIs, and perform data analysis. ArtBloom serves as a bridge, offering not just code but insights into the “why” and “how” of choosing Python for modern backend development.

Through this hands-on experience, you’ve acquired tools and knowledge that are transferable to a range of Python projects. As you continue your journey, remember to experiment, optimize, and adapt the concepts and techniques you’ve learned here. Whether you’re integrating Python into existing workflows or building new systems from scratch, the skills gained from ArtBloom will empower you to deliver backend solutions that are as elegant as they are efficient.

For further exploration and technical details, refer to the ArtBloom GitHub repository. 🚀


Discover more from Code, Craft & Community

Subscribe to get the latest posts sent to your email.

Leave a Reply

Discover more from Code, Craft & Community

Subscribe now to keep reading and get access to the full archive.

Continue reading