from operator import truediv
Demo project for the Iommi web framework
You need uv,
the Python package/version/virtual environment manager.
If you don't already have it, recommend installing it using pipx:
pipx install uv(pipx installs packages as tools in their own virtual environment, link if you don't already have it.)
Clone the repo to your computer:
git clone git@github.com:smangham/iommi-demoThen enter the directory, and copy the template environment file .env.example:
cd iommi-demo
cp .env.example .envInitialise the virtual environment and install the development prerequisites into it. Then set up the database and load the demo data:
uv venv
make develop
make migrations
make fixturesYou can now run the server with:
make serverGo to http://localhost:8000, and look around.
In order to sign in, you'll need to register the app with Google's OAuth. Follow the process in the AllAuth docs:
- Create a new Google OAuth client.
- Add the client ID and secret to your
.envfile. - Add
http://localhostandhttp://localhost:8000to the OAuth client as authorised JavaScript origins and redirect URIs. - Restart the server.
Once you've signed in, you can register your account as a superuser:
djmanage makestaff your_email@gmail.com --superuser(This is just a shortcut to python manage.py from the correct directory)
Now you have access to the admin interface, and can manually view and edit things.
The template I've used has linting/code style fixing built in. Check out Makefile;
make fix-py will run the ruff code formatter over your code.
Run it before committing and fix/flag as ignored anything it can't fix itself.
This uses Iommi, a very powerful and flexible framework for building websites in Python. The documentation is here, but it can be a bit hard to get your head around. The cookbook pages are a good thing to refer to.
Examples are good as well, so I'll just outline a few key concepts to understand the code:
There's basically two ways of doing anything - procedural and declarative. Example - building a form to edit a specific instance of a model, procedurally:
view = Form(
auto__model=MyDjangoModel,
instance=lambda params, **_: params.my_django_model
)and declaratively, where the class Meta is basically just what you'd give as arguments:
class MyForm(Form):
class Meta:
auto__model=MyDjangoModel
instance=lambda params, **_: params.my_django_model
view = MyForm()In this example, the stuff in app.main_menu.py is mostly procedural,
and there's an example of a more complicated view in app.pages.py (that gets included into main_menu).
Everything is built out of 'refinables'. For example, Form has some settings for automatically generating it from a model.
Example - Building a form to edit only the name and height of `MyDjangoModel:
Form(
auto=dict{
model=MyDjangoModel
include=['name', 'height']
}
)But you can also skip providing the dict of options with just double underscores:
Form(
auto__model=MyDjangoModel,
auto__include=['name', 'height']
)Anything not specified will have its default value.
A lot of arguments can be provided as functions that evaluate at runtime. Example - Building a form that's only editable if you're staff:
Form(
editable=lambda user, **_: user.is_staff
)class MyForm(Form):
class Meta:
editable=lambda user, **_: user.is_staffIf you need the functions to be more complicated, then in the procedural frame you can declare them elsewhere. Example - Making a form editable only by Steve. Needs to make sure the user is signed in, so they have a name to check.
def staff_and_steve(user) -> bool:
if not user.is_authenticated:
return False
else if user.is_staff and user.first_name == 'Steve':
return True
return False
view = Form(
editable=lambda user, **_: staff_and_steve(user)
)Or declaratively:
class MyForm(Form):
class Meta:
@staticmethod
def editable(user, **_) -> bool:
if not user.is_authenticated:
return False
else if user.is_staff and user.first_name == 'Steve':
return True
return False
view = MyForm()The functions are passed a set of parameters. The values available vary, but it's generally request,
params (which contains any view parameters), user (a shortcut to request.user) and any you've specified using
path decoding (where you register a model, and then paths containing it put it in the context).
See app.apps.AppConfig and app.main_menu -> cats.items.detail.
The app has object-level permissions, applied using django-rules.
You can define the rules in rules.py, and create permissions based off of them.
They fit pretty well into Iommi's structure, so it's easy to check when setting up a URL or whatever if you're allowed to view/edit/whatever the object.
Note
This library was generated using copier from the Base Python Project Template repository.