• Stars
    star
    141
  • Rank 259,971 (Top 6 %)
  • Language
    Python
  • License
    MIT License
  • Created almost 4 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A small Django application to advertise the fun htmx can bring you

django-htmx-fun

A small Django application to advertise the fun htmx can bring you.

It implements a Single-Page-Application for writing a diary.

The entries in the diary database get lazy loaded (endless scrolling) via hx-trigger="revealed"

Why I love htmx?

If you look into the past then one thing is clear: stateless has won. Nobody starts a new project with Corba these days. Stateless http is the winner.

I don't understand why JavaScript based frontend frameworks seem to be the only way for new projects.

I want the client/browser to be SSS (simple, stupid and stateless).

I need to validate my data on the server anyway. So why should I validate them on the client?

The Django Forms library has all you need to write database focused applications.

Sending HTML fragments over the wire keeps my application simple.

There is just one thing which is outdated (although it is still perfectly fine). The need for a full page refresh after submitting a form.

I want html pages with several small forms and I want to load and submit each of them individually. This does not mean I want to write a Single-Page-Application. There are more colors than black and white.

For more about htmx see the homepage: htmx.org

HTMX: Frontend Revolution (Slides from DjangoCon 2021)

Youtube video of the Talk DjangoCon US 2021: HTMX, Frontend Revolution

Install

If you want to install my small htmx demo application:

python3 -m venv django-htmx-fun-env
cd django-htmx-fun-env
. bin/activate
pip install -e git+https://github.com/guettli/django-htmx-fun.git#egg=django-htmx-fun

The source code is now in src/django-htmx-fun/

Run Database Migrations

manage.py migrate

Start Webserver

manage.py runserver

Diary: http://127.0.0.1:8000/

Admin

manage.py createsuperuser

Admin: http://127.0.0.1:8000/admin

No need for the POST/Redirect/GET pattern

If you are used to django's form handling, then you are used to the POST/Redirect/GET Pattern. This means after the client submitted a valid form, the server response has the http status 302 with a new location URL.

This is not needed if you submit a form via htmx and you just want to update one part of the whole page.

I use this pattern now:

Case 1: The http POST was successful, and data was changed. The server returns the status code 201 "Created". I use this even if data was changed, and not "created". I use this and not the usual 200 to simplify testing. I never ever want to confuse the http status of a successful htmx POST with the http status of an invalid traditional django http POST. The response contains the new HTML. No need for a redirect.

Case 2: The http POST was not successful, since the data in the form was not valid. Then my server code returns 422.

Related question: Which http status codes to use when processing http post?

Full Page (aka "client-side") Redirect

If you use htmx, then most http responses will contains html fragments which will get swapped into the current page.

But sometimes you want to do a traditional full page redirect. In the htmx docs it is called "client-side" redirect.

Then you need return a http response which has the http header "HX-Redirect" set to the URL of the new location. Docs: Response Headers

A common mistake is the set the status code if this response to 302. But this will trigger a redirect inside htmx.

Here is some code to do a full page redirect with Django: hx-target: swap html vs full page reload

Naming Pattern

Here is my personal naming pattern, which helps me to read the source more easily

_page():

Function based view.

foo_page(request, ...).

Returns a HttpResponse with a full page.

URL: /foo

This servers only http GET. Updates (http POST) go to _hxpost URLs.


_hx():

Function based view.

foo_hx(request, ...)

This method should be called via HTTP-GET. Returns a HttpResponse which only contains a HTML fragment.

URL: /foo_hx


_hxpost():

Function based view.

foo_hxpost(request, ...)

This methods should be called via HTTP-POST. Returns a HttpResponse which only contains a HTML fragment.

URL: /foo_hxpost

It makes sense to use the require_POST decorator,if you have concerns that a GET request (where request.POST is empty) could accidently change data.


_json():

Function based view.

foo_json(request, ...)

This method returns a JSONResponse.

URL: /foo_json

TODO: I am not happy with this yet, since you can't distinguish between a method which returns a JSON data (dictionary), JSON string or JSONResponse.


_html():

Python method which returns a HTML SafeString.

Usually created via format_html().

Flat URL Namespace

Above naming pattern makes it very use to get to the corresponding code.

Imagine you get a message from tool monitoring your servers. There is an exception at URL "/sunshine/123", then I know the name of the method which handles this URL. The method is "sunshine_page()".

If you need several pages for a model, then you will not use "/sunshine/foo" and "/sunshine/bar", but instead "/sunshine_foo" and "/sunshine_bar".

Opinionated Best Practices

I switched from Django class-based-views (CBV) to function-based-views (FBV). This simplifies things. One URL corresponds to one Python method. If an action requires two HTTP verbs (GET and POST), then I use two URLs. Posts always go to hx-methods, not to URLs returning full pages.

I like it conditionless. I try to avoid to have too many "if" and "else".

I avoid to use if request.method == 'POST'. This means I don't handle different http verbs in one function based view. A function based view handles either GET xor a POST. URLs are cheap I create two URLs if I need a a readonly view and a view which does something.

I only use the http verbs GET and POST, although htmx can do http PUT, http PATCH, http DELETE, ...

I don't use the special http headers which get added by htmx. I avoid this (pseudo code): "if request is a htmx request, then ...". Instead I create two endpoints: One which returns a full page, one which returns a fragment.

Goodbye formsets. I use several <form> tags in one page. This means I hardly use formsets. Some for the "prefix" of forms: Since I don't put several Django form instances into one <form> tag, I don't need the prefix any more.

Screenshot

diary-django-htmx

In devtools you can see the lazy loading of the endless scrolling

... All this is possible without writing a single line of JavaScript :-)

Pull Requests are welcome

You have an idea how to improve this example? Great! Just do it, provide a pull request and I will merge it.

Related

More Repositories

1

programming-guidelines

My personal programming guidelines
275
star
2

open-source-paas

Open Source PaaS List
105
star
3

deadends-of-it

Deadends of Information Technology
75
star
4

django-tips

Güttli's opinionated Django Tips
Python
67
star
5

frow--fragments-over-the-wire

HTML over the wire: List of frameworks which receive HTML snippets from the server.
58
star
6

python-tips

Güttli's opinionated Python Tips
15
star
7

wol

Working out Loud
15
star
8

ten-flying-fingers

My goal is to keep the pointing fingers on "F" and "J" as much as possible
8
star
9

github-travis-bumpversion-pypi

Four steps: github commit, travis CI, bumpversion, Upload to pypi
8
star
10

html_form_to_dict

HTML Form to Dict
Python
8
star
11

fix-CVE-2020-15228

Fix CVE-2020-15228 (set-env, add-path in Github-Actions)
Python
6
star
12

git-tips

My opinionated tips about Git
5
star
13

panorama

Create panorama in one run. Uses hugin for the real work.
Python
4
star
14

django-check-html-middleware

Django middleware to check the html created by your application (during development)
Python
4
star
15

wysiwyg

List of WYSIWYG editors
3
star
16

headset

My long journey to a suitable headset
3
star
17

golang-and-vscode

Golang and VSCode
3
star
18

htmx-jsfiddle-examples

HTMX JSFiddle Examples
3
star
19

NextWiki

NextWiki should be a Wiki which can be easily integrated into Nextcloud
3
star
20

stoic-trace-cockpit

Django Trace Cockpit
Python
2
star
21

dumpenv

dumpenv: Dump values of the current Python environment
Python
2
star
22

python-name2type-mapping

Idea to map variable names in code to type information
2
star
23

less-is-more

Less is more: How to reduce code size and make it easier to read.
2
star
24

why-i-dont-like-templates

Why I don't like to create HTML with templates
2
star
25

web-development

Web Development
2
star
26

slow-boring-clear-fast

Slow-boring-clear-fast: A simple Decision-making technique
2
star
27

why-i-like-django-and-sap

Why I like Django and why I like SAP
2
star
28

reprec

reprec: Recursively replace strings in files and other goodies
Python
2
star
29

intranets

Intranets: My point of view
2
star
30

shell-tips

I like the command line: My favorite tips and tricks
1
star
31

tomofu

Together it's more fun
1
star
32

cognitive-biases

1
star
33

stoic-html

Stoic HTML: A simple wrapper around Django's `format_html()`
Python
1
star
34

generic-backend

Generic Backend: No more coding, just config
1
star
35

Microsoft-365-vs-Google-Workspace

Microsoft 365 vs Google Workspace
1
star
36

nomo

nomo: No more coding for simple database applications
Python
1
star
37

wrap_and_log_calls

Wrapper to log all calls to a linux command
Python
1
star
38

local-grafana

Local Grafana/Loki/Promtail Setup
1
star
39

front-end-frameworks

List of front-end frameworks
1
star
40

how-to-create-html

How to create HTML with Python
1
star
41

python-custom-strings

Python Custom Strings (pre PEP)
1
star
42

sbecho

Second Brain Echo
1
star
43

server-responsibilities

List of responsibilities for running servers
1
star
44

wikis

List of wiki software
1
star
45

alternative-to-a-chair

Alternative to a chair
1
star
46

lets-fix-js

Let's fix JavaScript
1
star
47

subx

The subx package provides a data structure for results of subprocess calls.
Python
1
star
48

passive-protocol-of-thoughts

Passive protocol of thoughts. A mindfulness exercise.
1
star