Skip to content

Conversation

@zahraaalizadeh
Copy link
Contributor

Description

This PR introduces a CLI wrapper named booster to simplify the process of initializing Django projects:

  • Simplifies project initialisation with reusable configurations.
  • Introduces flexibility for customising templates and project variants.
  • Lays the groundwork for future CLI enhancements.

Key updates

  1. Add CLI Wrapper: Implements a command-line interface (booster) for generating Django projects based on a YAML configuration file.

  2. Dynamic Template Rendering:

  • Replaces the use of django-admin startproject with direct rendering of templates using Jinja2.
  • Supports dynamic customisation of directory names, file names, and content based on config file values.
  • Handles Django-specific template tags (e.g., verbatim) using Jinja2’s raw tag for compatibility.
  1. Configuration: Default configuration file is sample_config.yml.

  2. Package Creation:

  • Converts the CLI into an installable Python package.
  • Includes packaging instructions with pyproject.toml for pipx installation.
  1. Documentation Updates:
  • Updates the README to reflect the new CLI usage.
  • Records architectural decisions in an ADR document.
  1. Housekeeping: Adds .gitignore entries for build artifacts and output folders.

@zahraaalizadeh zahraaalizadeh changed the title Add a CLI Wrapper for Django Project Initialization with Template Rendering Add a CLI Wrapper for Django Project Initialization Nov 22, 2024
@zahraaalizadeh
Copy link
Contributor Author

I noted I need to update the project CI properly. I'll fix it some time next week.

@zahraaalizadeh zahraaalizadeh force-pushed the cli-wrapper/basic-step branch 4 times, most recently from 3da0029 to 37ca747 Compare November 22, 2024 06:06
@a-musing-moose
Copy link
Contributor

Question: Do you need to replace startproject entirely?

Could you just wrap it in your script then perform additional steps after startproject has finished?

This change adds a simple wrapper for startproject
command of django. It generate the project based on
the config file.
After this change, the path to the project
can be configured in the config file.

If the path doesn't exists it creates the path.
If the path exists, it prompts the user to confirm
a fresh start or abort.
@zahraaalizadeh zahraaalizadeh force-pushed the cli-wrapper/basic-step branch 2 times, most recently from a3f441b to decc714 Compare November 29, 2024 07:40
@zahraaalizadeh
Copy link
Contributor Author

@a-musing-moose Thanks for the review! My initial idea was to use Jinja2 for rendering settings dynamically, but I agree that sticking with startproject provides a more straightforward and familiar approach. I’ve updated the PR so the CLI tool now acts as a simple wrapper around startproject. In upcoming PRs, I’ll focus on post-processing steps, such as custom database setup and other enhancements. Let me know if there’s anything you’d like me to prioritise or adjust. 😊

from pathlib import Path

import typer
import yaml
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (non-blocking): ‏Can we use toml instead?

I have a pet peeve about yaml - I find it hard to format correctly for some reason. Toml is a simpler standard, we already use it (pyproject.toml) and as an added bonus - reading toml is built into recent versions of Python: https://docs.python.org/3/library/tomllib.html

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. I'll update it.

license = {text = "BSD-3-Clause"}
dependencies = [
"typer[all]>=0.9.0",
"jinja2>=3.1.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: I don't think jinja2 is needed any more

If we don't need it - please remove.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops!

@app.command()
def fire(
config_path: Path = typer.Option(
DEFAULT_CONFIG,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: ‏Make this a mandatory argument

The script cannot proceed without a config file. A default for the name of a project for example is never going to be reasonable. So might as well make it a mandatory argument.


def django_start_project(config):
"""Create a Django project using the specified configuration."""
project_name = config["project_name"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Check this is a valid Python package name

Since project_name is used as the package name, it needs to be a valid. We should do some basic checks here to ensure it is:

  • a-z, 0-9
  • Lowercase
  • underscores

Comment on lines +10 to +12
BASE_DIR = Path(__file__).resolve().parent.parent
OUTPUT_DIR = BASE_DIR / "output"
DEFAULT_CONFIG = BASE_DIR / "sample_config.yml"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Remove these defaults.

As already mentioned - I don't think a default config file makes sense. So I also think that the output dir should either be the current working directory or come from the now mandatory config file.

This is being designed as a stand alone script, installed from a wheel using pipx. As such the BASE_DIR as described here might not actually exist as it is being run from inside a pipx virtualenv all by itself. ‏

"--extension",
"py,env,sh,toml,yml",
"--exclude",
"nothing",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: ‏Perhaps a comment here as to why we are using --exclude nothing

Copy link
Contributor

@jadedarko jadedarko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if alternatives were considered to a custom wrapper tool?

The first thing that comes to mind is django-cookiecutter, which is a widely used tool that wraps project initiation in a similar way

The ADR should address alternatives like this to avoid people asking the above question at the very least ;)

@a-musing-moose
Copy link
Contributor

I'm wondering if alternatives were considered to a custom wrapper tool?

The first thing that comes to mind is django-cookiecutter, which is a widely used tool that wraps project initiation in a similar way

The ADR should address alternatives like this to avoid people asking the above question at the very least ;)

Agreed - the ADR should cover this.

In terms of using cookie-cutter... we could but the intent is that a follow up to what is in here is the addition of "variants". Cookie cutter requires that all possible configures and content are baked into the templates - including/excluding portions of those templates. For me this tips over into unreadable pretty quickly. What I am hoping we can do with our own wrapper script is to avoid that. Details on how are still up for debate - but I can foresee "variants" having their own little config files that can do things like adding lines to the INSTALLED_APPS or appending lines to settings in general. As well as copying files across. So hopefully keeping the core template fairly simple whilst allowing it to be augmented.

This may all be a tall ask - but I thought it worth exploring. Very much open to suggestions though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants