Skip to content

Commit 4ebecd1

Browse files
committed
Symlink CLAUDE.md, add README instructions and do some README updates
1 parent 48caf10 commit 4ebecd1

File tree

53 files changed

+1737
-371
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1737
-371
lines changed

AGENTS.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,20 @@ This is the only AGENTS.md file in the repo -- you don't need to look for others
1313

1414
Inside each top level subdirectory is a `README.md` that is a symlink to the `README.md` in the of the Python package itself. You only need to edit the `README.md` inside of the package itself.
1515

16-
The README is the main written documentation for the package (or module). You can structure it using [`plain/assets/README.md`](/plain/plain/assets/README.md) as an example.
16+
The README is the main written documentation for the package (or module). You can structure it using [`plain-api/plain/api/README.md`](/plain/plain/assets/README.md) as an example.
17+
18+
Some instructions for writing READMEs:
19+
20+
- Underneath the h1 should be a **<short description>** (in bold) that describes the package in one sentence.
21+
- There should be a table of contents at the top with links to the h2s and h3s in the README.
22+
- When referencing specific classes or functions in code, link to them with a # fragment identifier, like this: [`AssetView`](./views.py#AssetView).
23+
- The first section of the README should be an **Overview** that gets straight into basic examples of how to use the package.
24+
- An **Installation** section should always be present but it should be the last section of the README.
25+
- The **Installation** section should get the user from nothing to _something_, even if _something_ is a demo/example view or code that will need additional customization.
26+
- The **Installation** steps will typically be run by an agent like Claude Code, but they also need to be written in a way that a human can follow them.
27+
- Miscellaneous notes or information about the package should be added as **FAQs** in the second to last section of the README.
28+
- For the **FAQs**, use h4s for the questions.
29+
- The most advanced usages of the package don't need to be fully documented (i.e. every possible paramater, etc.). They can be mentioned if the user otherwise wouldn't know about them, but then they can be linked to the code itself for more information.
1730

1831
## Verifying changes
1932

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AGENTS.md

plain-admin/plain/admin/README.md

Lines changed: 103 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -2,100 +2,22 @@
22

33
**Manage your app with a backend interface.**
44

5-
The Plain Admin provides a combination of built-in views and the flexibility to create your own. You can use it to quickly get visibility into your app's data and to manage it.
6-
7-
![Plain Admin user example](https://assets.plainframework.com/docs/plain-pageviews-user.png)
5+
- [Overview](#overview)
6+
- [Admin viewsets](#admin-viewsets)
7+
- [Admin cards](#admin-cards)
8+
- [Admin forms](#admin-forms)
9+
- [List displays](#list-displays)
10+
- [Toolbar](#toolbar)
11+
- [Impersonate](#impersonate)
12+
- [Installation](#installation)
813

9-
## Installation
10-
11-
Install the `plain.admin` package and its dependencies.
14+
## Overview
1215

13-
```console
14-
uv add plain.admin
15-
```
16-
17-
The admin uses a combination of other Plain packages, most of which you will already have installed. Ultimately, your settings will look something like this:
18-
19-
```python
20-
# app/settings.py
21-
INSTALLED_PACKAGES = [
22-
"plain.models",
23-
"plain.tailwind",
24-
"plain.auth",
25-
"plain.sessions",
26-
"plain.htmx",
27-
"plain.admin",
28-
"plain.elements",
29-
# other packages...
30-
]
31-
32-
AUTH_USER_MODEL = "users.User"
33-
AUTH_LOGIN_URL = "login"
34-
35-
MIDDLEWARE = [
36-
"plain.sessions.middleware.SessionMiddleware",
37-
"plain.auth.middleware.AuthenticationMiddleware",
38-
"plain.admin.AdminMiddleware",
39-
]
40-
```
41-
42-
Your User model is expected to have an `is_admin` field (or attribute) for checking who has permission to access the admin.
43-
44-
```python
45-
# app/users/models.py
46-
from plain import models
47-
48-
49-
@models.register_model
50-
class User(models.Model):
51-
is_admin = models.BooleanField(default=False)
52-
# other fields...
53-
```
54-
55-
To make the admin accessible, add the `AdminRouter` to your root URLs.
56-
57-
```python
58-
# app/urls.py
59-
from plain.admin.urls import AdminRouter
60-
from plain.urls import Router, include, path
61-
62-
from . import views
63-
64-
65-
class AppRouter(Router):
66-
namespace = ""
67-
urls = [
68-
include("admin/", AdminRouter),
69-
path("login/", views.LoginView, name="login"),
70-
path("logout/", LogoutView, name="logout"),
71-
# other urls...
72-
]
73-
74-
```
75-
76-
Optionally, you can add the admin toolbar to your base template. The toolbar will appear when `settings.DEBUG` or when `request.user.is_admin` (including in production!).
77-
78-
```html
79-
<!-- app/templates/base.html -->
80-
<!DOCTYPE html>
81-
<html lang="en">
82-
<head>
83-
<meta charset="UTF-8">
84-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
85-
<title>{{ html_title|default("My App") }}</title>
86-
{% tailwind_css %}
87-
</head>
88-
<body>
89-
{% block content required %}{% endblock %}
90-
91-
{% toolbar %}
92-
</body>
93-
</html>
94-
```
16+
The Plain Admin provides a combination of built-in views and the flexibility to create your own. You can use it to quickly get visibility into your app's data and to manage it.
9517

96-
## Admin viewsets
18+
![Plain Admin user example](https://assets.plainframework.com/docs/plain-pageviews-user.png)
9719

98-
The most common use of the admin is to display and manage your `plain.models`. To do this, create a viewset with a set of inner views.
20+
The most common use of the admin is to manage your `plain.models`. To do this, create a [viewset](./views/viewsets.py#AdminViewset) with inner/nested views:
9921

10022
```python
10123
# app/users/admin.py
@@ -140,7 +62,9 @@ class UserAdmin(AdminViewset):
14062
form_class = UserForm
14163
```
14264

143-
The [`AdminViewset`](./views/viewsets.py) will automatically recognize inner views named `ListView`, `CreateView`, `DetailView`, `UpdateView`, and `DeleteView`. It will interlink these views automatically in the UI and form success URLs. You can define additional views too, but you will need to implement a couple methods to hook them up.
65+
## Admin viewsets
66+
67+
The [`AdminViewset`](./views/viewsets.py#AdminViewset) will automatically recognize inner views named `ListView`, `CreateView`, `DetailView`, `UpdateView`, and `DeleteView`. It will interlink these views automatically in the UI and form success URLs. You can define additional views too, but you will need to implement a couple methods to hook them up.
14468

14569
## Admin cards
14670

@@ -150,9 +74,9 @@ TODO
15074

15175
TODO
15276

153-
## List `displays`
77+
## List displays
15478

155-
On admin list views, you can define different `displays` to build predefined views of your data. The display choices will be shown in the UI, and you can use the current `self.display` in your view.
79+
On [`AdminListView`](./views/objects.py#AdminListView) and [`AdminModelListView`](./views/models.py#AdminModelListView), you can define different `displays` to build predefined views of your data. The display choices will be shown in the UI, and you can use the current `self.display` in your view logic.
15680

15781
```python
15882
# app/users/admin.py
@@ -188,3 +112,89 @@ TODO
188112
## Impersonate
189113

190114
TODO
115+
116+
## Installation
117+
118+
Install the `plain.admin` package from [PyPI](https://pypi.org/project/plain.admin/):
119+
120+
```bash
121+
uv add plain.admin
122+
```
123+
124+
The admin uses a combination of other Plain packages, most of which you will already have installed. Ultimately, your settings will look something like this:
125+
126+
```python
127+
# app/settings.py
128+
INSTALLED_PACKAGES = [
129+
"plain.models",
130+
"plain.tailwind",
131+
"plain.auth",
132+
"plain.sessions",
133+
"plain.htmx",
134+
"plain.admin",
135+
"plain.elements",
136+
# other packages...
137+
]
138+
139+
AUTH_USER_MODEL = "users.User"
140+
AUTH_LOGIN_URL = "login"
141+
142+
MIDDLEWARE = [
143+
"plain.sessions.middleware.SessionMiddleware",
144+
"plain.auth.middleware.AuthenticationMiddleware",
145+
"plain.admin.AdminMiddleware",
146+
]
147+
```
148+
149+
Your User model is expected to have an `is_admin` field (or attribute) for checking who has permission to access the admin.
150+
151+
```python
152+
# app/users/models.py
153+
from plain import models
154+
155+
156+
@models.register_model
157+
class User(models.Model):
158+
is_admin = models.BooleanField(default=False)
159+
# other fields...
160+
```
161+
162+
To make the admin accessible, add the [`AdminRouter`](./urls.py#AdminRouter) to your root URLs.
163+
164+
```python
165+
# app/urls.py
166+
from plain.admin.urls import AdminRouter
167+
from plain.urls import Router, include, path
168+
169+
from . import views
170+
171+
172+
class AppRouter(Router):
173+
namespace = ""
174+
urls = [
175+
include("admin/", AdminRouter),
176+
path("login/", views.LoginView, name="login"),
177+
path("logout/", LogoutView, name="logout"),
178+
# other urls...
179+
]
180+
```
181+
182+
Typically you will also want to add the admin `{% toolbar %}` to the bottom of your base template. The toolbar will appear when `settings.DEBUG` or when `request.user.is_admin` (including in production!).
183+
184+
```html
185+
<!-- app/templates/base.html -->
186+
<!DOCTYPE html>
187+
<html lang="en">
188+
<head>
189+
<meta charset="UTF-8">
190+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
191+
<title>{{ html_title|default("My App") }}</title>
192+
{% tailwind_css %}
193+
</head>
194+
<body>
195+
{% block content required %}{% endblock %}
196+
197+
{% toolbar %}
198+
</body>
199+
</html>
200+
```

plain-admin/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "plain.admin"
33
version = "0.37.2"
4-
description = "Admin dashboard and tools for Plain."
4+
description = "Manage your app with a backend interface."
55
authors = [{name = "Dave Gaeddert", email = "dave.gaeddert@dropseed.dev"}]
66
license = "BSD-3-Clause"
77
readme = "README.md"

plain-api/plain/api/README.md

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,18 @@
22

33
**Build APIs using class-based views.**
44

5-
This package includes lightweight view classes for building APIs using the same patterns as regular HTML views. It also provides an [`APIKey` model](#api-keys) and support for generating [OpenAPI](#openapi) documents.
5+
- [Overview](#overview)
6+
- [Authentication and authorization](#authentication-and-authorization)
7+
- [`PUT`, `POST`, and `PATCH`](#put-post-and-patch)
8+
- [`DELETE`](#delete)
9+
- [API keys](#api-keys)
10+
- [OpenAPI](#openapi)
11+
- [Deploying](#deploying)
12+
- [Installation](#installation)
13+
14+
## Overview
15+
16+
This package includes lightweight view classes for building APIs using the same patterns as regular HTML views. It also provides an [`APIKey`](./models.py#APIKey) model and support for generating [OpenAPI](#openapi) documents.
617

718
Because [Views](/plain/plain/views/README.md) can convert built-in types to responses, an API view can simply return a dict or list to send a JSON response back to the client. More complex responses can use the [`JsonResponse`](/plain/plain/http/response.py#JsonResponse) class.
819

@@ -179,7 +190,7 @@ class PullRequestView(BaseAPIView):
179190

180191
## API keys
181192

182-
The provided [`APIKey` model](./models.py) includes randomly generated, unique API tokens that are automatically parsed by `APIKeyView`. The tokens can optionally be named and include an `expires_at` date.
193+
The provided [`APIKey`](./models.py#APIKey) model includes randomly generated, unique API tokens that are automatically parsed by `APIKeyView`. The tokens can optionally be named and include an `expires_at` date.
183194

184195
Associating an `APIKey` with a user (or team, for example) is up to you. Most likely you will want to use a `ForeignKey` or a `ManyToManyField`.
185196

@@ -218,7 +229,7 @@ user.api_key = APIKey.objects.create()
218229
user.save()
219230
```
220231

221-
To use API keys in your views, you can inherit from `APIKeyView` and customize the [`use_api_key` method](./views.py#use_api_key) to set the `request.user` attribute (or any other attribute) to the object associated with the API key.
232+
To use API keys in your views, you can inherit from `APIKeyView` and customize the [`use_api_key`](./views.py#use_api_key) method to set the `request.user` attribute (or any other attribute) to the object associated with the API key.
222233

223234
```python
224235
# app/api/views.py
@@ -380,3 +391,64 @@ class APIRouter(Router):
380391
path("openapi.json", AssetView.as_view(asset_path="openapi.json")),
381392
]
382393
```
394+
395+
## Installation
396+
397+
Install the `plain.api` package from [PyPI](https://pypi.org/project/plain.api/):
398+
399+
```console
400+
$ uv add plain.api
401+
```
402+
403+
Typically you will want to create an `api` package to contain all of the views and URLs for your app's API.
404+
405+
```console
406+
$ plain create api
407+
```
408+
409+
The `app.api` package should be added to your app's `INSTALLED_APPS` setting in `app/settings.py`:
410+
411+
```python
412+
# app/settings.py
413+
INSTALLED_APPS = [
414+
# ...other apps
415+
"app.api",
416+
]
417+
```
418+
419+
Then create a your API URL router and your first API view.
420+
421+
```python
422+
# app/api/urls.py
423+
from plain.urls import Router, path
424+
from plain.api.views import APIView
425+
426+
427+
class ExampleAPIView(APIView):
428+
def get(self):
429+
return {"message": "Hello, world!"}
430+
431+
432+
class APIRouter(Router):
433+
namespace = "api"
434+
urls = [
435+
path("example/", ExampleAPIView),
436+
]
437+
```
438+
439+
The `APIRouter` can then be included in your app's URLs.
440+
441+
```python
442+
# app/urls.py
443+
from plain.urls import include, path
444+
445+
from .api.urls import APIRouter
446+
447+
448+
class AppRouter(Router):
449+
namespace = "app"
450+
urls = [
451+
# ...other routes
452+
include("api/", APIRouter),
453+
]
454+
```

plain-api/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "plain.api"
33
version = "0.12.0"
4-
description = "API for Plain."
4+
description = "Build APIs using class-based views."
55
authors = [{name = "Dave Gaeddert", email = "dave.gaeddert@dropseed.dev"}]
66
readme = "README.md"
77
requires-python = ">=3.11"

0 commit comments

Comments
 (0)