djelm


Namedjelm JSON
Version 0.10.0 PyPI version JSON
download
home_pageNone
SummaryA framework for using Elm programs in a Django project
upload_time2024-04-18 02:17:39
maintainerNone
docs_urlNone
authorConfidenceman02
requires_python<4.0,>=3.11
licenseNone
keywords django elm
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <img alt="ContourPy" src="https://raw.githubusercontent.com/confidenceman02/django-elm/main/assets/icon.png" width="140">

# Djelm

[![Actions status](https://Confidenceman02.github.io/djelm/workflows/CI/badge.svg)](https://github.com/Confidenceman02/django-elm/actions)
[![](https://img.shields.io/badge/license-MIT-blue)](https://github.com/Confidenceman02/django-elm/blob/main/LICENSE)

> [!NOTE]
> Djelm is an active project and prior to a stable release changes might come hard and fast as we explore and learn the best way to integrate Elm programs into Django.
>
> Much effort is put into the [CHANGELOG](https://github.com/Confidenceman02/django-elm/blob/main/CHANGELOG.md) to describe and advise on all changes between versions so be sure to utilise it when updating to the latest and greatest version.

# Elm integration for Django a.k.a. Django + Elm = 💚

---

## Table of Content

- [The why](#the-why)
- [The when](#the-when)
- [Requirements](#requirements)
- [Elm setup](#elm-setup)
- [Django setup](#django-setup)
- [Djelm setup](#djelm-setup)
- [Your first Elm program](#your-first-elm-program)
  - [create Command](#create-command)
  - [addprogram Command](#addprogram-command)
  - [npm Command](#npm-command)
  - [watch Command](#watch-command)
  - [compile Command](#compile-command)
  - [compilebuild Command](#compilebuild-command)
  - [Template tags](#template-tags)
  - [Flags](#flags)
  - [Flag classes](#flag-classes)
  - [generatemodel Command](#generatemodel-command)
- [Widgets](#widgets)
  - [addwidget Command](#addwidget-command)
  - [listwidgets Command](#addwidget-command)
- [Elm resources](#elm-resources)

# The why

Django is an awesome framework for rapidly building web apps, but it can be tricky to build UI's that require a level of
reactivity unable to
be expressed via templates alone.

Elm is a statically and strongly typed language with an approachable syntax that provides exceptional programming
ergonomics to build highly reactive UI's.
Elm programs are robust, reliable, and famously, delightful to write and maintain!

Djelm provides the bridge for both of these wonderful technologies to converge allowing you to seamlessly build the
dynamic parts of your UI whilst
working with the Django conventions you know and love.

[Back to top](#table-of-content)

# The when

Because djelm is **not intended** to be the primary UI solution for your Django project, the following guidelines serve
as an initial checklist
that will ensure your use of djelm is fit for purpose.

1. Use the Django conventions and tools.

   - The Django ecosystem has many great libraries that can help you organise tricky UI for forms, tables, pagination,
     and search. You get the benefit of these tools not needing a UI framework.

2. Try out a killer combo such as [HTMX](https://htmx.org/) and [Alpine JS](https://alpinejs.dev/).

   - This combo being able to handle a huge of amount of your UI reactivity is entirely conceivable, however, consider
     the following.
     - HTMX: Ensure your UI/UX won't suffer as a result of a roundtrip to the server.
     - Alpine JS: If you find yourself writing app logic akin to that of a framework, djelm is likely a far better option.

[Back to top](#table-of-content)

# Requirements

- Elm 0.19.1
- Python >=3.11
- Django >= 4.2
- Node >= 16.4

[Back to top](#table-of-content)

# Elm setup

Djelm will expect the Elm binary to be in your `PATH`.

Head on over to the [installation guide](https://guide.elm-lang.org/install/elm.html) to get the Elm binary on your
system.

After installing, let's make sure Elm is ready to go. In your terminal run the command:

```bash
elm
```

You should see a friendly welcome message and some other helpful Elm info.

[Back to top](#table-of-content)

# Django setup

If you don't have a Django project ready to go, we will need to take care of some initial setup.

It's best practice to create a python virtual environment:

```bash
python -m venv venv
```

Then let's start the virtual environment:

```bash
source venv/bin/activate
```

Let's get the Django package with pip:

```bash
pip install django
```

Create the django project:

```bash
django-admin startproject djelmproject && cd djelmproject
```

Lets look at what `startproject` created:

```bash
djelmproject
├── manage.py
├── djelmproject
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   ├── asgi.py
│   └── wsgi.py
```

[Back to top](#table-of-content)

# Djelm setup

You will need the djelm package, let's get it with `pip`.

```bash
pip install djelm
```

Add the djelm app to your `INSTALLED_APPS` in `settings.py`

```python
# settings.py

INSTALLED_APPS = [
    ...,
    "djelm",
]

```

Optionally set your package manager of choice.

> [!NOTE]
> If you don't set this variable then djelm will try to use [pnpm](https://pnpm.io/). Use
> the [install guide](https://pnpm.io/installation) if you would like to use this default and you don't currently
> have [pnpm](https://pnpm.io/) installed.
>
> We **highly** recommend you use `pnpm` as it doesn't install node package peer dependencies, and usually provides a hassle free performant experience.

```python
# settings.py

NODE_PACKAGE_MANAGER = "yarn"  # npm, pnpm (default)
```

[Back to top](#table-of-content)

# Your first Elm program

## `create` Command

The first thing we will need to do is create a directory where all our Elm programs will live. Djelm is fairly
opinionated about what lives inside this directory so for the best experience let's use the `create` command.

From your Django project root:

```bash
python manage.py djelm create elm_programs
```

> [!TIP]
> The `elm_programs` argument is just a name that I give the app that all my Elm programs live in, feel free to
> call it something else for your project. For the purposes of this tutorial I will refer to the `elm_programs` app.

If we take a look at our `elm_programs` app directory let's see what was created for us.

```bash
elm_programs
├── apps.py
├── elm_programs.djelm
├── flags
├── static
│   └── dist
├── static_src
│   ├── .gitignore
│   ├── djelm_src
│   ├── elm.json
│   ├── package.json
│   └── src
│       └── Models
└── templatetags
    └── __init__.py
```

What you are seeing is the directory structure required for djelm to seamlessly work with both Django and Elm.

Everything outside of the `static_src` directory should look like a typical Django app, and everything inside
of `static_src` should look like a conventional Elm project, with some extra bits.

## `addprogram` Command

Now that we have a place for Elm programs to live let's go ahead and add one!

```bash
python manage.py djelm addprogram elm_programs Main
```

> [!TIP]
> You can change the `Main` argument to whatever makes the most sense for your program, like `Map`, `TodoApp`, `UserProfile`.
>
> For the most predictable results when running the `addprogram` command, ensure you use the
> Elm module naming conventions which you can find [here](https://guide.elm-lang.org/webapps/modules).

Looking at the `elm_programs` app directory again we can see a few things have been added.

> [!NOTE]
> Added files marked with \*

```bash
elm_programs
├── apps.py
├── elm_programs.djelm
├── flags
│   └── main.py *
├── static
│   └── dist
├── static_src
│   ├── .gitignore
│   ├── djelm_src
│   │   └── Main.ts *
│   ├── elm.json
│   ├── package.json
│   └── src
│       ├── Main.elm *
│       └── Models
│           └── Main.elm *
└── templatetags
    ├── __init__.py
    └── main_tags.py *
```

Jump in to the `static_src/src/Main.elm` file in the `elm_programs` directory and what you will see is a simple Elm
program. You
might be able to work out what this program does just by looking at the `Msg` type!

```elm
type Msg
    = Increment
    | Decrement
```

## `npm` Command

To actually run this Elm program with Django we will need to compile it, for that we will need to install the node
packages defined in the `elm_programs` `package.json` file.

> [!NOTE]
> Elm doesn't actually need node to compile programs. However, djelm optimzes Elm programs to work with Django templates
> so a tiny amount of DOM binding code is bundled in.

We can install all node packages with the following command:

```bash
python manage.py djelm npm elm_programs install
```

> [!NOTE]
> The above command runs `pnpm install` in the `elm_programs/static_src` directory. `pnpm` will be substituted for
> whatever is in the `NODE_PACKAGE_MANAGER` variable in `settings.py`.

alternatively you could do the following:

```bash
cd elm_programs/static_src/ && pnpm install
```

More generally, you can use any arguments you want for the `npm` command after the `elm_programs` argument:

```bash
# pnpm, yarn

python manage.py djelm npm elm_programs add -D some-cool-npm-package

# npm

python manage.py djelm npm elm_programs install -D some-cool-npm-package
```

## `watch` Command

After all node packages have installed we can use the djelm `watch` strategy to compile our Elm programs and watch for
changes.

```bash
python manage.py djelm watch elm_programs
```

You should see some output like the following:

```bash
 Built 2 bundles in 100ms!
```

Let's take a look at what changed.

```bash
elm_programs
├── apps.py
├── elm_programs.djelm
├── flags
│   └── main.py
├── static
│   ├── dist
│   │   └── Main.47ea7fa8.js *
│   │   └── Main.47ea7fa8.js.map *
│   │   └── Main.js *
│   │   └── Main.js.map *
├── static_src
│   ├── .gitignore
│   ├── djelm_src
│   │   └── Main.ts
│   ├── elm.json
│   ├── package.json
│   └── src
│       ├── Main.elm
│       └── Models
│           └── Main.elm
└── templatetags
    ├── __init__.py
    └── main_tags.py

```

Djelm compiled our `Main.elm` program and bundled it up for us in a place where Django can work with it, awesome!

## `compile` Command

To compile our programs as a one off command we can use the following:

```bash
python manage.py djelm compile elm_programs
```

## `compilebuild` Command

Use the `compilebuild` command for generating production quality assets.

From the command line run:

```bash
python manage.py djelm compilebuild elm_programs
```

> [!TIP]
> After you have compiled your Elm programs for a production environment, it is advised that you
> remove the `static_src` directory as it will contain cache files and node modules that you probably
> don't want sitting on your Django production server taking up space.

## Template tags

Let's now actually render something in the browser by adding our `Main` programs tags to a Django template.

> [!NOTE]
> I have added the following `base.html` and `main.html` templates to a `elm_programs/templates` directory for demonstration purposes.
> You will need to create this directory if you havent done so already.
>
> If you already have a django project you can just add the tags into whatever templates you want to render the elm program.

```html
<!-- base.html -->

<!doctype html>
<html lang="en">
  <head>
    <title>Djelm</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    {% block head %}{% endblock %}
  </head>
  <body>
    {% block content %}{% endblock %}
  </body>
</html>
```

```djangohtml
<!-- main.html -->

{% extends "base.html" %}
{% load static %}
{% load static main_tags %}
{% block head %}
    {% include_main %}
{% endblock %}
{% block content %}
    <h1>Django + Elm = ❤️</h1>
    {% render_main %}
{% endblock %}
```

Then we can point a url to the `main.html` template file.

```python
# urls.py
from django.urls import path
from django.views.generic import TemplateView

urlpatterns = [
    path("", TemplateView.as_view(template_name="main.html")),
]
```

We can now start the Django server.

```bash
python manage.py runserver
```

Head on over to `http://127.0.0.1:8000/` and Voilà!

![example](https://Confidenceman02.github.io/djelm/static/djelm.gif)

## Flags

> [!NOTE]
> Flags are a critical paradigm in Elm so I recommend checking
> out [these docs](https://guide.elm-lang.org/interop/flags.html) for a great introduction to them.

Flags are the way we pipe data from our Django server in to our Elm programs. They very much define the contract between
python values and an Elm model.

Let's analyze the default program we generated earlier which simply increments and decrements an integer.

You will notice if we look at the running program in the browser that it starts off with an integer of `0`, but `0` is
not hardcoded anywhere in the Elm program.
So where does it come from?

This value actually comes bound to our render tag when we server render the template, and is passed to the Elm program
when it is initialized on the client,
as a flag value.

Check out the `elm_programs/templatetags/main_tags.py` file that was generated for you:

```python
@register.inclusion_tag("djelm/program.html", takes_context=True)
def render_main(context):
    return {"key": key, "flags": MainFlags.parse(0)}
```

Those experienced with Django might be having an 'Aha!' moment right now but don't worry if thats not the case,
I'll explain everything.

This `0` value is set from the `render_main` tag function as a default, the actual values you will want to pass your Elm
programs are much more likely to originate in your Django views like the following example:

```python
# views.py

def counter_view(request: AuthedHttpRequest):
    context = {
        "counter_start": 0,
    }

    return render(request, "base.html", context)
```

The context that was set in the view is available to us from the `render_main` tag function which we can
call `MainFlags` with:

```python
# main_tags.py

@register.inclusion_tag("djelm/program.html", takes_context=True)
def render_main(context):
    return {"key": key, "flags": MainFlags.parse(context["counter_start"])}
```

Whilst in this example we are hardcoding a value of `0`, you really can pass it any `int` you want, perhaps an ID from a
database
model or some other computed value.

The one constraint you have is that it must be an `int`, which is perfect for our default Elm
program, but not very flexible for other types of Elm programs you might like to build.

> [!NOTE]
> If you are not sure what Django tags are check out the
> docs [here](https://docs.djangoproject.com/en/5.0/howto/custom-template-tags/).

Let's try passing something that isn't an `int` and see what happens:

```python
# main_tags.py

@register.inclusion_tag("djelm/program.html", takes_context=True)
def render_main(context):
    return {"key": key, "flags": MainFlags.parse("Hello Elm!")}
```

What you should be seeing is a server error, but why? Let's find out!

## Flag classes

If we inspect the `MainFlags` function in `elm_programs/flags/main.py` we will see the following:

```python
MainFlags = Flags(IntFlag())
```

The `Flags` class takes as it's argument the flags shape that you want to pass to the `Main.elm` program
when it initializes.

We are using the `IntFlag` class which enforces that the type of value that can be passed to our `Main.elm` program is
an `int`.
If we pass anything other than an `int` to `MainFlags.parse` an error will be raised just like the one we saw when we used the
value `MainFlags.parse("hello Elm!")`.

The `IntFlag` class is intrinsic to the model that exists in our `Main.elm` program as seen
in `elm_programs/static_src/src/Models/Main.elm`:

```elm
type alias ToModel =
    Int


toModel : Decode.Decoder ToModel
toModel =
    Decode.int
```

The `Main.elm` program uses this model to protect us from any uninvited flags that might show up at runtime as seen in `elm_programs/static_src/src/Main.elm`:

```elm
init : Value -> ( Model, Cmd Msg )
init f =
    case decodeValue toModel f of
        Ok m -> -- Flag decoded to an Int, Success!
            ( Ready m, Cmd.none )

        Err _ -> -- Flag failed to decode to an Int, Error!
            ( Error, Cmd.none )
```

To summarize, we get compile and runtime guarantees that our program will behave as we expect. This is what makes Elm programs
incredibly robust and nearly impossible to produce a runtime exception with.

You can check out all the python flags you can use to express your data shape [here](https://github.com/Confidenceman02/django-elm/blob/main/src/djelm/flags/README.md).

## `generatemodel` Command

Our python flag classes have a little trick up their sleeve that makes keeping both our python and Elm flag contracts in
sync for us.

We can see that in the `elm_programs/static_src/src/Models/Main.elm` module we have the following comment:

```elm
{-| Do not manually edit this file, it was auto-generated by djelm
<https://github.com/Confidenceman02/django-elm>
-}
```

That's right! Djelm makes it uneccessary to ever need to write your own flag decoders, which can be a hefty task if the
shape of your flags is complicated.
Whatever we define as our flags shape in Python will determine the decoders we find in our Elm program.

Let's put this to the test and change the `IntFlag` class to something else for our default program:

```python
MainFlags = Flags(StringFlag())
```

Then in the console run:

```bash
python manage.py djelm generatemodel elm_programs Main
```

> [!NOTE]
> If you have run the `watch` command, djelm will automatically detect flag changes and generate the models for you.

Looking in `elm_programs/static_src/src/Models/Main.elm` you can see our decoders have changed to handle a `String`
value:

```elm
type alias ToModel =
    String


toModel : Decode.Decoder ToModel
toModel =
    Decode.string
```

We can get more adventurous to really see the heavy lifting djelm is doing for us:

```python
MainFlags = Flags(
    ObjectFlag(
        {
            "geometry": ListFlag(ListFlag(FloatFlag())),
            "token": NullableFlag(StringFlag()),
            "someObject": ObjectFlag({"hello": StringFlag(), "elm": StringFlag()}),
        }
    )
)
```

And the result of running `python manage.py djelm generatemodel elm_programs Main`:

```elm
type alias ToModel =
    { geometry : (List (List Float))
    , token : Maybe String
    , someObject : SomeObject_
    }

type alias SomeObject_ =
    { hello : String
    , elm : String
    }


toModel : Decode.Decoder ToModel
toModel =
    Decode.succeed ToModel
        |>  required "geometry" (Decode.list (Decode.list Decode.float))
        |>  required "token" (Decode.nullable Decode.string)
        |>  required "someObject" someObject_Decoder

someObject_Decoder : Decode.Decoder SomeObject_
someObject_Decoder =
    Decode.succeed SomeObject_
        |>  required "hello" Decode.string
        |>  required "elm" Decode.string

```

> [!TIP]
> The default Elm program we generated will fail to compile when performing the model changes above.
>
> To help you understand why it failed you can run the [compile](#compile-command) command and experience first hand
> the beauty that is an Elm compiler message.

# Widgets

Djelm widgets are feature rich, highly dynamic and customizable programs we can use to supercharge our UI's.

The widgets are added and live inside your app similar to that of a regular program that was added with the [addprogram](#addprogram-command) command.
The key difference being that they are purpose built, handsomely styled, and ready to be used straight out of the box.

Let's consider this simple model and form.

```python
# models.py
from django.db import models

class Course(models.Model):
    name = models.CharField(max_length=100)
    instructor = models.CharField(max_length=100)

    def __str__(self) -> str:
        return self.name


class Promotion(models.Model):
    courses = models.ForeignKey(Course, on_delete=models.SET_NULL, null=True)

# forms.py
from django import forms

class PromotionForm(forms.ModelForm):
    courses = forms.ModelChoiceField(
        queryset=Course.objects.all(),
    )

    class Meta:
        model = Promotion
        fields = ["courses"]
```

Django will conveniently render a completely usable UI with very little effort from us.

![example](https://Confidenceman02.github.io/djelm/static/dj-mcf.gif)

It's a great start but we want something more customizable, and a fair bit fancier. It's widget time!

## `listwidgets` Command

Let's see a list of all the djelm widgets

```bash
python manage.py djelm listwidgets
```

The `ModelChoiceField` widget from this list seems like the one we want.

## `addwidget` Command

Let's add the widget to our `elm_programs` app.

```bash
python manage.py djelm addwidget elm_programs ModelChoiceField
```

We will also need to compile the widget program before we can use it.

```bash
python manage.py djelm compile elm_programs
```

> [!NOTE]
> If you have run the [watch](#watch-command) command, djelm will automatically compile the program for you.

Now that we have a widget program to render let's set a custom template for our form field.

```python
# forms.py
from django import forms

class PromotionForm(forms.ModelForm):
    courses = forms.ModelChoiceField(
        queryset=Course.objects.all(),
        template_name="modelChoiceFieldWidget.html", # <--- Added
    )

    class Meta:
        model = Promotion
        fields = ["courses"]
```

Let's now create that custom template.

```djangohtml
<!-- modelChoiceFieldWidget.html -->

{% extends "base.html" %}
{% load modelChoiceField_widget_tags %}
{% block head %}
    {% include_ModelChoiceFieldWidget %}
{% endblock %}
{% block content %}
    {{ field.label_tag }}
    {% render_ModelChoiceFieldWidget %}
{% endblock %}
```

Now that's pretty handsome!

![example](https://Confidenceman02.github.io/djelm/static/djelm-mcf.gif)

We get a nice style and feature improvement over the default input Django gives us, but can we do better?

Yes we can!

Because widget programs are just normal Elm programs that live inside your djelm app, you have complete control
to customize them as you see fit should they not quite fit your branding or presentational requirements.

Let's delight our users by giving them more information about their choices.

![example](https://Confidenceman02.github.io/djelm/static/djelm-cs-mcf.gif)

That's a good looking widget!

> [!NOTE]
> For specific documentation and advanced features for widgets, check out the widget [documentation](https://github.com/Confidenceman02/django-elm/blob/main/src/djelm/forms/widgets/README.md).

[Back to top](#table-of-content)

## Elm resources

- [Official Elm site](https://elm-lang.org/)

  You can find a wonderful introduction to Elm, as well as all the information required to start writing elm programs.
  There is also an [online editor](https://elm-lang.org/try) to try out writing your own programs from scratch or from
  example programs.

- [Elm community Slack](https://elm-lang.org/community/slack)

  The official place for Elm news, jobs, and genreal chit chat.

- [Elm in action](https://www.manning.com/books/elm-in-action)

  By far the most comprehensive book on Elm that exists, written by the mighty Richard Feldman, he will take you from
  beginner to expert.

- [Front end Masters course](https://frontendmasters.com/courses/intro-elm/)

  Here's that Richard Feldman guy again! Richard explores all the juicy bits of Elm in a very high quality presentation.

- [Elm discourse](https://discourse.elm-lang.org/)

  Join our wonderful and friendly Elm forum community! Ask questions, check out what people are working on, or just just
  say hi!

- [Incremental Elm](https://incrementalelm.com/chat/)

  The most experienced and smartest of our community tend to hang in here. Extremely welcoming, and lots of great
  channels to join.

[Back to top](#table-of-content)

2023 © [Confidenceman02 - A Full Stack Django Developer and Elm shill](#)


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "djelm",
    "maintainer": null,
    "docs_url": null,
    "requires_python": "<4.0,>=3.11",
    "maintainer_email": null,
    "keywords": "django, elm",
    "author": "Confidenceman02",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/6b/4d/8b87da6c61af8f1989750276f1324894be47e836dfb3006fc2b73d7a990e/djelm-0.10.0.tar.gz",
    "platform": null,
    "description": "<img alt=\"ContourPy\" src=\"https://raw.githubusercontent.com/confidenceman02/django-elm/main/assets/icon.png\" width=\"140\">\n\n# Djelm\n\n[![Actions status](https://Confidenceman02.github.io/djelm/workflows/CI/badge.svg)](https://github.com/Confidenceman02/django-elm/actions)\n[![](https://img.shields.io/badge/license-MIT-blue)](https://github.com/Confidenceman02/django-elm/blob/main/LICENSE)\n\n> [!NOTE]\n> Djelm is an active project and prior to a stable release changes might come hard and fast as we explore and learn the best way to integrate Elm programs into Django.\n>\n> Much effort is put into the [CHANGELOG](https://github.com/Confidenceman02/django-elm/blob/main/CHANGELOG.md) to describe and advise on all changes between versions so be sure to utilise it when updating to the latest and greatest version.\n\n# Elm integration for Django a.k.a. Django + Elm = \ud83d\udc9a\n\n---\n\n## Table of Content\n\n- [The why](#the-why)\n- [The when](#the-when)\n- [Requirements](#requirements)\n- [Elm setup](#elm-setup)\n- [Django setup](#django-setup)\n- [Djelm setup](#djelm-setup)\n- [Your first Elm program](#your-first-elm-program)\n  - [create Command](#create-command)\n  - [addprogram Command](#addprogram-command)\n  - [npm Command](#npm-command)\n  - [watch Command](#watch-command)\n  - [compile Command](#compile-command)\n  - [compilebuild Command](#compilebuild-command)\n  - [Template tags](#template-tags)\n  - [Flags](#flags)\n  - [Flag classes](#flag-classes)\n  - [generatemodel Command](#generatemodel-command)\n- [Widgets](#widgets)\n  - [addwidget Command](#addwidget-command)\n  - [listwidgets Command](#addwidget-command)\n- [Elm resources](#elm-resources)\n\n# The why\n\nDjango is an awesome framework for rapidly building web apps, but it can be tricky to build UI's that require a level of\nreactivity unable to\nbe expressed via templates alone.\n\nElm is a statically and strongly typed language with an approachable syntax that provides exceptional programming\nergonomics to build highly reactive UI's.\nElm programs are robust, reliable, and famously, delightful to write and maintain!\n\nDjelm provides the bridge for both of these wonderful technologies to converge allowing you to seamlessly build the\ndynamic parts of your UI whilst\nworking with the Django conventions you know and love.\n\n[Back to top](#table-of-content)\n\n# The when\n\nBecause djelm is **not intended** to be the primary UI solution for your Django project, the following guidelines serve\nas an initial checklist\nthat will ensure your use of djelm is fit for purpose.\n\n1. Use the Django conventions and tools.\n\n   - The Django ecosystem has many great libraries that can help you organise tricky UI for forms, tables, pagination,\n     and search. You get the benefit of these tools not needing a UI framework.\n\n2. Try out a killer combo such as [HTMX](https://htmx.org/) and [Alpine JS](https://alpinejs.dev/).\n\n   - This combo being able to handle a huge of amount of your UI reactivity is entirely conceivable, however, consider\n     the following.\n     - HTMX: Ensure your UI/UX won't suffer as a result of a roundtrip to the server.\n     - Alpine JS: If you find yourself writing app logic akin to that of a framework, djelm is likely a far better option.\n\n[Back to top](#table-of-content)\n\n# Requirements\n\n- Elm 0.19.1\n- Python >=3.11\n- Django >= 4.2\n- Node >= 16.4\n\n[Back to top](#table-of-content)\n\n# Elm setup\n\nDjelm will expect the Elm binary to be in your `PATH`.\n\nHead on over to the [installation guide](https://guide.elm-lang.org/install/elm.html) to get the Elm binary on your\nsystem.\n\nAfter installing, let's make sure Elm is ready to go. In your terminal run the command:\n\n```bash\nelm\n```\n\nYou should see a friendly welcome message and some other helpful Elm info.\n\n[Back to top](#table-of-content)\n\n# Django setup\n\nIf you don't have a Django project ready to go, we will need to take care of some initial setup.\n\nIt's best practice to create a python virtual environment:\n\n```bash\npython -m venv venv\n```\n\nThen let's start the virtual environment:\n\n```bash\nsource venv/bin/activate\n```\n\nLet's get the Django package with pip:\n\n```bash\npip install django\n```\n\nCreate the django project:\n\n```bash\ndjango-admin startproject djelmproject && cd djelmproject\n```\n\nLets look at what `startproject` created:\n\n```bash\ndjelmproject\n\u251c\u2500\u2500 manage.py\n\u251c\u2500\u2500 djelmproject\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 __init__.py\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 settings.py\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 urls.py\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 asgi.py\n\u2502   \u2514\u2500\u2500 wsgi.py\n```\n\n[Back to top](#table-of-content)\n\n# Djelm setup\n\nYou will need the djelm package, let's get it with `pip`.\n\n```bash\npip install djelm\n```\n\nAdd the djelm app to your `INSTALLED_APPS` in `settings.py`\n\n```python\n# settings.py\n\nINSTALLED_APPS = [\n    ...,\n    \"djelm\",\n]\n\n```\n\nOptionally set your package manager of choice.\n\n> [!NOTE]\n> If you don't set this variable then djelm will try to use [pnpm](https://pnpm.io/). Use\n> the [install guide](https://pnpm.io/installation) if you would like to use this default and you don't currently\n> have [pnpm](https://pnpm.io/) installed.\n>\n> We **highly** recommend you use `pnpm` as it doesn't install node package peer dependencies, and usually provides a hassle free performant experience.\n\n```python\n# settings.py\n\nNODE_PACKAGE_MANAGER = \"yarn\"  # npm, pnpm (default)\n```\n\n[Back to top](#table-of-content)\n\n# Your first Elm program\n\n## `create` Command\n\nThe first thing we will need to do is create a directory where all our Elm programs will live. Djelm is fairly\nopinionated about what lives inside this directory so for the best experience let's use the `create` command.\n\nFrom your Django project root:\n\n```bash\npython manage.py djelm create elm_programs\n```\n\n> [!TIP]\n> The `elm_programs` argument is just a name that I give the app that all my Elm programs live in, feel free to\n> call it something else for your project. For the purposes of this tutorial I will refer to the `elm_programs` app.\n\nIf we take a look at our `elm_programs` app directory let's see what was created for us.\n\n```bash\nelm_programs\n\u251c\u2500\u2500 apps.py\n\u251c\u2500\u2500 elm_programs.djelm\n\u251c\u2500\u2500 flags\n\u251c\u2500\u2500 static\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 dist\n\u251c\u2500\u2500 static_src\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 .gitignore\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 djelm_src\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 elm.json\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 package.json\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 src\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 Models\n\u2514\u2500\u2500 templatetags\n    \u2514\u2500\u2500 __init__.py\n```\n\nWhat you are seeing is the directory structure required for djelm to seamlessly work with both Django and Elm.\n\nEverything outside of the `static_src` directory should look like a typical Django app, and everything inside\nof `static_src` should look like a conventional Elm project, with some extra bits.\n\n## `addprogram` Command\n\nNow that we have a place for Elm programs to live let's go ahead and add one!\n\n```bash\npython manage.py djelm addprogram elm_programs Main\n```\n\n> [!TIP]\n> You can change the `Main` argument to whatever makes the most sense for your program, like `Map`, `TodoApp`, `UserProfile`.\n>\n> For the most predictable results when running the `addprogram` command, ensure you use the\n> Elm module naming conventions which you can find [here](https://guide.elm-lang.org/webapps/modules).\n\nLooking at the `elm_programs` app directory again we can see a few things have been added.\n\n> [!NOTE]\n> Added files marked with \\*\n\n```bash\nelm_programs\n\u251c\u2500\u2500 apps.py\n\u251c\u2500\u2500 elm_programs.djelm\n\u251c\u2500\u2500 flags\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.py *\n\u251c\u2500\u2500 static\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 dist\n\u251c\u2500\u2500 static_src\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 .gitignore\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 djelm_src\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Main.ts *\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 elm.json\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 package.json\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 src\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 Main.elm *\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 Models\n\u2502\u00a0\u00a0         \u2514\u2500\u2500 Main.elm *\n\u2514\u2500\u2500 templatetags\n    \u251c\u2500\u2500 __init__.py\n    \u2514\u2500\u2500 main_tags.py *\n```\n\nJump in to the `static_src/src/Main.elm` file in the `elm_programs` directory and what you will see is a simple Elm\nprogram. You\nmight be able to work out what this program does just by looking at the `Msg` type!\n\n```elm\ntype Msg\n    = Increment\n    | Decrement\n```\n\n## `npm` Command\n\nTo actually run this Elm program with Django we will need to compile it, for that we will need to install the node\npackages defined in the `elm_programs` `package.json` file.\n\n> [!NOTE]\n> Elm doesn't actually need node to compile programs. However, djelm optimzes Elm programs to work with Django templates\n> so a tiny amount of DOM binding code is bundled in.\n\nWe can install all node packages with the following command:\n\n```bash\npython manage.py djelm npm elm_programs install\n```\n\n> [!NOTE]\n> The above command runs `pnpm install` in the `elm_programs/static_src` directory. `pnpm` will be substituted for\n> whatever is in the `NODE_PACKAGE_MANAGER` variable in `settings.py`.\n\nalternatively you could do the following:\n\n```bash\ncd elm_programs/static_src/ && pnpm install\n```\n\nMore generally, you can use any arguments you want for the `npm` command after the `elm_programs` argument:\n\n```bash\n# pnpm, yarn\n\npython manage.py djelm npm elm_programs add -D some-cool-npm-package\n\n# npm\n\npython manage.py djelm npm elm_programs install -D some-cool-npm-package\n```\n\n## `watch` Command\n\nAfter all node packages have installed we can use the djelm `watch` strategy to compile our Elm programs and watch for\nchanges.\n\n```bash\npython manage.py djelm watch elm_programs\n```\n\nYou should see some output like the following:\n\n```bash\n Built 2 bundles in 100ms!\n```\n\nLet's take a look at what changed.\n\n```bash\nelm_programs\n\u251c\u2500\u2500 apps.py\n\u251c\u2500\u2500 elm_programs.djelm\n\u251c\u2500\u2500 flags\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.py\n\u251c\u2500\u2500 static\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 dist\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Main.47ea7fa8.js *\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Main.47ea7fa8.js.map *\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Main.js *\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Main.js.map *\n\u251c\u2500\u2500 static_src\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 .gitignore\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 djelm_src\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Main.ts\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 elm.json\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 package.json\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 src\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 Main.elm\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 Models\n\u2502\u00a0\u00a0         \u2514\u2500\u2500 Main.elm\n\u2514\u2500\u2500 templatetags\n    \u251c\u2500\u2500 __init__.py\n    \u2514\u2500\u2500 main_tags.py\n\n```\n\nDjelm compiled our `Main.elm` program and bundled it up for us in a place where Django can work with it, awesome!\n\n## `compile` Command\n\nTo compile our programs as a one off command we can use the following:\n\n```bash\npython manage.py djelm compile elm_programs\n```\n\n## `compilebuild` Command\n\nUse the `compilebuild` command for generating production quality assets.\n\nFrom the command line run:\n\n```bash\npython manage.py djelm compilebuild elm_programs\n```\n\n> [!TIP]\n> After you have compiled your Elm programs for a production environment, it is advised that you\n> remove the `static_src` directory as it will contain cache files and node modules that you probably\n> don't want sitting on your Django production server taking up space.\n\n## Template tags\n\nLet's now actually render something in the browser by adding our `Main` programs tags to a Django template.\n\n> [!NOTE]\n> I have added the following `base.html` and `main.html` templates to a `elm_programs/templates` directory for demonstration purposes.\n> You will need to create this directory if you havent done so already.\n>\n> If you already have a django project you can just add the tags into whatever templates you want to render the elm program.\n\n```html\n<!-- base.html -->\n\n<!doctype html>\n<html lang=\"en\">\n  <head>\n    <title>Djelm</title>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    {% block head %}{% endblock %}\n  </head>\n  <body>\n    {% block content %}{% endblock %}\n  </body>\n</html>\n```\n\n```djangohtml\n<!-- main.html -->\n\n{% extends \"base.html\" %}\n{% load static %}\n{% load static main_tags %}\n{% block head %}\n    {% include_main %}\n{% endblock %}\n{% block content %}\n    <h1>Django + Elm = \u2764\ufe0f</h1>\n    {% render_main %}\n{% endblock %}\n```\n\nThen we can point a url to the `main.html` template file.\n\n```python\n# urls.py\nfrom django.urls import path\nfrom django.views.generic import TemplateView\n\nurlpatterns = [\n    path(\"\", TemplateView.as_view(template_name=\"main.html\")),\n]\n```\n\nWe can now start the Django server.\n\n```bash\npython manage.py runserver\n```\n\nHead on over to `http://127.0.0.1:8000/` and Voil\u00e0!\n\n![example](https://Confidenceman02.github.io/djelm/static/djelm.gif)\n\n## Flags\n\n> [!NOTE]\n> Flags are a critical paradigm in Elm so I recommend checking\n> out [these docs](https://guide.elm-lang.org/interop/flags.html) for a great introduction to them.\n\nFlags are the way we pipe data from our Django server in to our Elm programs. They very much define the contract between\npython values and an Elm model.\n\nLet's analyze the default program we generated earlier which simply increments and decrements an integer.\n\nYou will notice if we look at the running program in the browser that it starts off with an integer of `0`, but `0` is\nnot hardcoded anywhere in the Elm program.\nSo where does it come from?\n\nThis value actually comes bound to our render tag when we server render the template, and is passed to the Elm program\nwhen it is initialized on the client,\nas a flag value.\n\nCheck out the `elm_programs/templatetags/main_tags.py` file that was generated for you:\n\n```python\n@register.inclusion_tag(\"djelm/program.html\", takes_context=True)\ndef render_main(context):\n    return {\"key\": key, \"flags\": MainFlags.parse(0)}\n```\n\nThose experienced with Django might be having an 'Aha!' moment right now but don't worry if thats not the case,\nI'll explain everything.\n\nThis `0` value is set from the `render_main` tag function as a default, the actual values you will want to pass your Elm\nprograms are much more likely to originate in your Django views like the following example:\n\n```python\n# views.py\n\ndef counter_view(request: AuthedHttpRequest):\n    context = {\n        \"counter_start\": 0,\n    }\n\n    return render(request, \"base.html\", context)\n```\n\nThe context that was set in the view is available to us from the `render_main` tag function which we can\ncall `MainFlags` with:\n\n```python\n# main_tags.py\n\n@register.inclusion_tag(\"djelm/program.html\", takes_context=True)\ndef render_main(context):\n    return {\"key\": key, \"flags\": MainFlags.parse(context[\"counter_start\"])}\n```\n\nWhilst in this example we are hardcoding a value of `0`, you really can pass it any `int` you want, perhaps an ID from a\ndatabase\nmodel or some other computed value.\n\nThe one constraint you have is that it must be an `int`, which is perfect for our default Elm\nprogram, but not very flexible for other types of Elm programs you might like to build.\n\n> [!NOTE]\n> If you are not sure what Django tags are check out the\n> docs [here](https://docs.djangoproject.com/en/5.0/howto/custom-template-tags/).\n\nLet's try passing something that isn't an `int` and see what happens:\n\n```python\n# main_tags.py\n\n@register.inclusion_tag(\"djelm/program.html\", takes_context=True)\ndef render_main(context):\n    return {\"key\": key, \"flags\": MainFlags.parse(\"Hello Elm!\")}\n```\n\nWhat you should be seeing is a server error, but why? Let's find out!\n\n## Flag classes\n\nIf we inspect the `MainFlags` function in `elm_programs/flags/main.py` we will see the following:\n\n```python\nMainFlags = Flags(IntFlag())\n```\n\nThe `Flags` class takes as it's argument the flags shape that you want to pass to the `Main.elm` program\nwhen it initializes.\n\nWe are using the `IntFlag` class which enforces that the type of value that can be passed to our `Main.elm` program is\nan `int`.\nIf we pass anything other than an `int` to `MainFlags.parse` an error will be raised just like the one we saw when we used the\nvalue `MainFlags.parse(\"hello Elm!\")`.\n\nThe `IntFlag` class is intrinsic to the model that exists in our `Main.elm` program as seen\nin `elm_programs/static_src/src/Models/Main.elm`:\n\n```elm\ntype alias ToModel =\n    Int\n\n\ntoModel : Decode.Decoder ToModel\ntoModel =\n    Decode.int\n```\n\nThe `Main.elm` program uses this model to protect us from any uninvited flags that might show up at runtime as seen in `elm_programs/static_src/src/Main.elm`:\n\n```elm\ninit : Value -> ( Model, Cmd Msg )\ninit f =\n    case decodeValue toModel f of\n        Ok m -> -- Flag decoded to an Int, Success!\n            ( Ready m, Cmd.none )\n\n        Err _ -> -- Flag failed to decode to an Int, Error!\n            ( Error, Cmd.none )\n```\n\nTo summarize, we get compile and runtime guarantees that our program will behave as we expect. This is what makes Elm programs\nincredibly robust and nearly impossible to produce a runtime exception with.\n\nYou can check out all the python flags you can use to express your data shape [here](https://github.com/Confidenceman02/django-elm/blob/main/src/djelm/flags/README.md).\n\n## `generatemodel` Command\n\nOur python flag classes have a little trick up their sleeve that makes keeping both our python and Elm flag contracts in\nsync for us.\n\nWe can see that in the `elm_programs/static_src/src/Models/Main.elm` module we have the following comment:\n\n```elm\n{-| Do not manually edit this file, it was auto-generated by djelm\n<https://github.com/Confidenceman02/django-elm>\n-}\n```\n\nThat's right! Djelm makes it uneccessary to ever need to write your own flag decoders, which can be a hefty task if the\nshape of your flags is complicated.\nWhatever we define as our flags shape in Python will determine the decoders we find in our Elm program.\n\nLet's put this to the test and change the `IntFlag` class to something else for our default program:\n\n```python\nMainFlags = Flags(StringFlag())\n```\n\nThen in the console run:\n\n```bash\npython manage.py djelm generatemodel elm_programs Main\n```\n\n> [!NOTE]\n> If you have run the `watch` command, djelm will automatically detect flag changes and generate the models for you.\n\nLooking in `elm_programs/static_src/src/Models/Main.elm` you can see our decoders have changed to handle a `String`\nvalue:\n\n```elm\ntype alias ToModel =\n    String\n\n\ntoModel : Decode.Decoder ToModel\ntoModel =\n    Decode.string\n```\n\nWe can get more adventurous to really see the heavy lifting djelm is doing for us:\n\n```python\nMainFlags = Flags(\n    ObjectFlag(\n        {\n            \"geometry\": ListFlag(ListFlag(FloatFlag())),\n            \"token\": NullableFlag(StringFlag()),\n            \"someObject\": ObjectFlag({\"hello\": StringFlag(), \"elm\": StringFlag()}),\n        }\n    )\n)\n```\n\nAnd the result of running `python manage.py djelm generatemodel elm_programs Main`:\n\n```elm\ntype alias ToModel =\n    { geometry : (List (List Float))\n    , token : Maybe String\n    , someObject : SomeObject_\n    }\n\ntype alias SomeObject_ =\n    { hello : String\n    , elm : String\n    }\n\n\ntoModel : Decode.Decoder ToModel\ntoModel =\n    Decode.succeed ToModel\n        |>  required \"geometry\" (Decode.list (Decode.list Decode.float))\n        |>  required \"token\" (Decode.nullable Decode.string)\n        |>  required \"someObject\" someObject_Decoder\n\nsomeObject_Decoder : Decode.Decoder SomeObject_\nsomeObject_Decoder =\n    Decode.succeed SomeObject_\n        |>  required \"hello\" Decode.string\n        |>  required \"elm\" Decode.string\n\n```\n\n> [!TIP]\n> The default Elm program we generated will fail to compile when performing the model changes above.\n>\n> To help you understand why it failed you can run the [compile](#compile-command) command and experience first hand\n> the beauty that is an Elm compiler message.\n\n# Widgets\n\nDjelm widgets are feature rich, highly dynamic and customizable programs we can use to supercharge our UI's.\n\nThe widgets are added and live inside your app similar to that of a regular program that was added with the [addprogram](#addprogram-command) command.\nThe key difference being that they are purpose built, handsomely styled, and ready to be used straight out of the box.\n\nLet's consider this simple model and form.\n\n```python\n# models.py\nfrom django.db import models\n\nclass Course(models.Model):\n    name = models.CharField(max_length=100)\n    instructor = models.CharField(max_length=100)\n\n    def __str__(self) -> str:\n        return self.name\n\n\nclass Promotion(models.Model):\n    courses = models.ForeignKey(Course, on_delete=models.SET_NULL, null=True)\n\n# forms.py\nfrom django import forms\n\nclass PromotionForm(forms.ModelForm):\n    courses = forms.ModelChoiceField(\n        queryset=Course.objects.all(),\n    )\n\n    class Meta:\n        model = Promotion\n        fields = [\"courses\"]\n```\n\nDjango will conveniently render a completely usable UI with very little effort from us.\n\n![example](https://Confidenceman02.github.io/djelm/static/dj-mcf.gif)\n\nIt's a great start but we want something more customizable, and a fair bit fancier. It's widget time!\n\n## `listwidgets` Command\n\nLet's see a list of all the djelm widgets\n\n```bash\npython manage.py djelm listwidgets\n```\n\nThe `ModelChoiceField` widget from this list seems like the one we want.\n\n## `addwidget` Command\n\nLet's add the widget to our `elm_programs` app.\n\n```bash\npython manage.py djelm addwidget elm_programs ModelChoiceField\n```\n\nWe will also need to compile the widget program before we can use it.\n\n```bash\npython manage.py djelm compile elm_programs\n```\n\n> [!NOTE]\n> If you have run the [watch](#watch-command) command, djelm will automatically compile the program for you.\n\nNow that we have a widget program to render let's set a custom template for our form field.\n\n```python\n# forms.py\nfrom django import forms\n\nclass PromotionForm(forms.ModelForm):\n    courses = forms.ModelChoiceField(\n        queryset=Course.objects.all(),\n        template_name=\"modelChoiceFieldWidget.html\", # <--- Added\n    )\n\n    class Meta:\n        model = Promotion\n        fields = [\"courses\"]\n```\n\nLet's now create that custom template.\n\n```djangohtml\n<!-- modelChoiceFieldWidget.html -->\n\n{% extends \"base.html\" %}\n{% load modelChoiceField_widget_tags %}\n{% block head %}\n    {% include_ModelChoiceFieldWidget %}\n{% endblock %}\n{% block content %}\n    {{ field.label_tag }}\n    {% render_ModelChoiceFieldWidget %}\n{% endblock %}\n```\n\nNow that's pretty handsome!\n\n![example](https://Confidenceman02.github.io/djelm/static/djelm-mcf.gif)\n\nWe get a nice style and feature improvement over the default input Django gives us, but can we do better?\n\nYes we can!\n\nBecause widget programs are just normal Elm programs that live inside your djelm app, you have complete control\nto customize them as you see fit should they not quite fit your branding or presentational requirements.\n\nLet's delight our users by giving them more information about their choices.\n\n![example](https://Confidenceman02.github.io/djelm/static/djelm-cs-mcf.gif)\n\nThat's a good looking widget!\n\n> [!NOTE]\n> For specific documentation and advanced features for widgets, check out the widget [documentation](https://github.com/Confidenceman02/django-elm/blob/main/src/djelm/forms/widgets/README.md).\n\n[Back to top](#table-of-content)\n\n## Elm resources\n\n- [Official Elm site](https://elm-lang.org/)\n\n  You can find a wonderful introduction to Elm, as well as all the information required to start writing elm programs.\n  There is also an [online editor](https://elm-lang.org/try) to try out writing your own programs from scratch or from\n  example programs.\n\n- [Elm community Slack](https://elm-lang.org/community/slack)\n\n  The official place for Elm news, jobs, and genreal chit chat.\n\n- [Elm in action](https://www.manning.com/books/elm-in-action)\n\n  By far the most comprehensive book on Elm that exists, written by the mighty Richard Feldman, he will take you from\n  beginner to expert.\n\n- [Front end Masters course](https://frontendmasters.com/courses/intro-elm/)\n\n  Here's that Richard Feldman guy again! Richard explores all the juicy bits of Elm in a very high quality presentation.\n\n- [Elm discourse](https://discourse.elm-lang.org/)\n\n  Join our wonderful and friendly Elm forum community! Ask questions, check out what people are working on, or just just\n  say hi!\n\n- [Incremental Elm](https://incrementalelm.com/chat/)\n\n  The most experienced and smartest of our community tend to hang in here. Extremely welcoming, and lots of great\n  channels to join.\n\n[Back to top](#table-of-content)\n\n2023 \u00a9 [Confidenceman02 - A Full Stack Django Developer and Elm shill](#)\n\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "A framework for using Elm programs in a Django project",
    "version": "0.10.0",
    "project_urls": null,
    "split_keywords": [
        "django",
        " elm"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8b29213de11c230199bec3633ceeed662b8082c5d6dc63254bde874afa20cf07",
                "md5": "a3da4e9d362558c841ba040527440a9e",
                "sha256": "4a4710def38f815aa1d6b37d3a809d7165787415d2868e1bead72a5e697fecdf"
            },
            "downloads": -1,
            "filename": "djelm-0.10.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "a3da4e9d362558c841ba040527440a9e",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": "<4.0,>=3.11",
            "size": 60375,
            "upload_time": "2024-04-18T02:17:37",
            "upload_time_iso_8601": "2024-04-18T02:17:37.415419Z",
            "url": "https://files.pythonhosted.org/packages/8b/29/213de11c230199bec3633ceeed662b8082c5d6dc63254bde874afa20cf07/djelm-0.10.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "6b4d8b87da6c61af8f1989750276f1324894be47e836dfb3006fc2b73d7a990e",
                "md5": "6d49c909ae6a9b6a609a1497b8cd723c",
                "sha256": "ec176459983f09c90f06d086dceca91d930bbce24a3519a58bd1f40b5d64052f"
            },
            "downloads": -1,
            "filename": "djelm-0.10.0.tar.gz",
            "has_sig": false,
            "md5_digest": "6d49c909ae6a9b6a609a1497b8cd723c",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": "<4.0,>=3.11",
            "size": 52635,
            "upload_time": "2024-04-18T02:17:39",
            "upload_time_iso_8601": "2024-04-18T02:17:39.246440Z",
            "url": "https://files.pythonhosted.org/packages/6b/4d/8b87da6c61af8f1989750276f1324894be47e836dfb3006fc2b73d7a990e/djelm-0.10.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-04-18 02:17:39",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "djelm"
}
        
Elapsed time: 0.23964s