# Let's create a django app using topdownrbac and DRF

### Base setup

Assuming all requirements are met, let's create the base django template:
```zsh
mkdir exampleapp                                  
django-admin startproject config exampleapp/

cd exampleapp
django-admin startapp example_app
```

We will be using uv as our package manager here, but you can use your favourite one (pip...)
```
uv init
uv venv
source .venv/bin/activate
uv add django-topdownrbac
uv add djangorestframework
```

Let's configure the settings in the config/settings.py file (add `topdownrbac`, `rest_framework`, `example_app` and `treebeard` (which is a required dependency) to `INSTALLED_APPS`:
```python
INSTALLED_APPS = [
    "topdownrbac",
    "rest_framework",
    "example_app",
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "treebeard",
]

AUTH_USER_MODEL = "topdownrbac.UserSubject"

AUTHENTICATION_BACKENDS = [
    "django.contrib.auth.backends.ModelBackend",
    "topdownrbac.backends.restricted_object_backend.RestrictedObjectBackend",
]

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework.authentication.SessionAuthentication",
        "rest_framework.authentication.BasicAuthentication",
    ],
}
```

### Making a basic app

Now let's create some basic classes which we want to protect in example_app/models.py.

```python
--8<-- "example_app/models.py"
```

Then run migrations:
```bash
uv run manage.py makemigrations example_app
uv run manage.py migrate
```

We'll also create the views and serializers, as well as other drf setup (we will stop at create / read for simplicity) :
```python
--8<-- "example_app/serializers.py"
```

```python
--8<-- "example_app/views.py"
```

```python
--8<-- "example_app/urls.py"
```

```python
--8<-- "config/urls.py"
```

### Setting up roles and permissions

With the app in place, let's create a management command to seed some example data. Create `example_app/management/commands/seed_data.py` (don't forget the `__init__.py` files):

```bash
mkdir -p example_app/management/commands
touch example_app/management/__init__.py
touch example_app/management/commands/__init__.py
```

```python
--8<-- "example_app/management/commands/seed_data.py"
```

Run it:
```bash
uv run manage.py seed_data
```

### Testing it through the API

Start the dev server and use curl (or any HTTP client):

```bash
uv run manage.py runserver
```

```bash
# List organizations alice can see
curl -u alice:secret http://localhost:8000/api/organizations/

# List projects bob can see (both projects -- Editor on the whole org tree)
curl -u bob:secret http://localhost:8000/api/projects/

# List projects charlie can see (only Website Redesign -- Viewer on that single project)
curl -u charlie:secret http://localhost:8000/api/projects/
```

Alice will see Acme Corp and both projects (view permission propagated from the org), while bob will also be able to modify them thanks to the Editor role. Charlie, however, can only see Website Redesign -- demonstrating that without `propagate=True` or a broader binding, access is limited to exactly the object the role was bound to.

Any new project created under Acme Corp will automatically inherit the propagated permissions -- no additional role bindings needed.

### Extending the user model with a profile

`topdownrbac` provides `UserSubject` as a ready-to-use concrete user model (set via `AUTH_USER_MODEL`). Rather than subclassing it, the recommended way to attach app-specific fields is the **profile pattern**: a separate model linked to `UserSubject` with a `OneToOneField`.

Add a `UserProfile` model in your app's `models.py`:

```python
--8<-- "example_app/models.py"
```

Then wire up a `post_save` signal so that a profile is automatically created whenever a new user is saved. Create `example_app/signals.py`:

```python
--8<-- "example_app/signals.py"
```

And register the signal in your app config (`example_app/apps.py`):

```python
--8<-- "example_app/apps.py"
```

Run migrations to create the new table:

```bash
uv run manage.py makemigrations example_app
uv run manage.py migrate
```

That's it. You can now read and write profile fields on any user:

```python
from topdownrbac.models import UserSubject

user = UserSubject.objects.get(username="alice")
user.profile.bio = "Alice is an org-wide viewer."
user.profile.save()

print(user.profile.bio)       # "Alice is an org-wide viewer."
print(user.profile.avatar_url) # ""
```

This approach keeps the library's `UserSubject` untouched while letting each consuming project add whatever fields it needs. The profile is accessible via `user.profile` (the `related_name`) and can be efficiently fetched with `select_related("profile")` in querysets.
