# Django Plugin

Invoke Zato services from Django views, models or any other Python code.

Zato provides a Django plugin that lets your Django applications invoke Zato services directly from views, models, or any other Python code.

This means your Django application can delegate all integration work to Zato. Instead of writing code in Django to connect to external APIs, databases, message queues, or other systems, you call a Zato service that handles it for you.

**Installation**

## Installing the plugin {#installing-the-plugin}

Install the plugin from [PyPI](https://pypi.org/project/django-zato-plugin/):

```bash
pip install django-zato-plugin
```

## Configuration {#configuration}

Add the following settings to your Django `settings.py`. By default, use the same password that you use to log in to your Zato Dashboard, i.e. the one from the `Zato_Password` environment variable.

```python
ZATO_URL = 'http://localhost:11223/zato/api/invoke/django/{}'
ZATO_USERNAME = 'django'
ZATO_PASSWORD = 'your-zato-password'
```

### Settings reference {#settings-reference}

| Setting | Description | Default |
| --- | --- | --- |
| `ZATO_URL` | URL of the Zato Django channel endpoint | `http://localhost:11223/zato/api/invoke/django/{}` |
| `ZATO_USERNAME` | Username for authentication | `django` |
| `ZATO_PASSWORD` | Password for authentication | (none) |

## Zato server configuration {#zato-server-configuration}

- Zato automatically creates the `/django` channel when the server starts
- No additional configuration is needed on the Zato side
- For production, you can use a separate password by setting `Zato_Django_Password` in both Django's `settings.py` and in the Zato container

## Next steps {#next-steps}

- [Invoking services](https://zato.io/integrations/django.html#)
- [Examples](https://zato.io/integrations/django.html#)
- [Middleware](https://zato.io/integrations/django.html#)

**Invoking services**

## The client.invoke method {#the-clientinvoke-method}

The plugin provides a single method to call any Zato service:

```python
from django_zato import client

response = client.invoke(service, data=None, request=None)
```

### Parameters {#parameters}

| Parameter | Type | Description |
| --- | --- | --- |
| `service` | string | Name of the Zato service to invoke |
| `data` | dict or None | Request data to send to the service |
| `request` | Django request or None | Optional Django request for context propagation |

### Return value {#return-value}

The method returns a dictionary containing the response from the Zato service.

### Exceptions {#exceptions}

The method raises `requests.HTTPError` if the request fails.

## Basic usage {#basic-usage}

Import the client and call `invoke` with the service name and request data:

```python
from django_zato import client

response = client.invoke('my.service', {'key': 'value'})
print(response)
```

## Usage in views {#usage-in-views}

A typical pattern is to call Zato services from Django views:

```python
from django.http import JsonResponse
from django_zato import client

def get_customer(request, customer_id):
    response = client.invoke('crm.customer.get', {'customer_id': customer_id})
    return JsonResponse(response)
```

## Handling responses {#handling-responses}

The response is a dictionary. Access fields directly:

```python
from django.http import JsonResponse
from django_zato import client

def create_order(request):
    order_data = {
        'customer_id': request.POST['customer_id'],
        'product_id': request.POST['product_id'],
        'quantity': int(request.POST['quantity']),
    }

    response = client.invoke('orders.create', order_data)

    order_id = response['order_id']
    total = response['total']

    return JsonResponse({
        'order_id': order_id,
        'total': total,
    })
```

## Error handling {#error-handling}

Wrap calls in try/except to handle errors:

```python
from django.http import JsonResponse
from django_zato import client
from requests import HTTPError

def get_customer(request, customer_id):
    try:
        response = client.invoke('crm.customer.get', {'customer_id': customer_id})
        return JsonResponse(response)
    except HTTPError as e:
        return JsonResponse({'error': str(e)}, status=e.response.status_code)
```

## Next steps {#next-steps_1}

- [Installation](https://zato.io/integrations/django.html#)
- [Examples](https://zato.io/integrations/django.html#)
- [Middleware](https://zato.io/integrations/django.html#)

**Examples**

## Cybersecurity automation {#cybersecurity-automation}

This example shows a security operations dashboard that coordinates blocking malicious IPs across a firewall and logging incidents in a SIEM system.

```python
# views.py
from django.http import JsonResponse
from django_zato import client

def block_ip(request):

    # Extract the request ..
    ip_address = request.POST['ip_address']
    reason = request.POST['reason']

    # .. block on firewall and get the response ..
    firewall_response = client.invoke('firewall.block-ip', {'ip_address': ip_address})
    rule_id = firewall_response['rule_id']

    # .. log incident in SIEM ..
    siem_response = client.invoke('siem.log-incident', {
        'ip_address': ip_address,
        'reason': reason,
        'action': 'blocked',
        'firewall_rule_id': rule_id,
    })

    # .. Extract the response from SIEM ..
    incident_id = siem_response['incident_id']

    # .. and return the response to the frontend.
    return JsonResponse({
        'status': 'ok',
        'firewall_rule_id': rule_id,
        'siem_incident_id': incident_id,
    })
```

## Flight information display {#flight-information-display}

This example shows a flight status board that retrieves real-time flight data and gate assignments.

```python
from django.http import JsonResponse
from django_zato import client

def get_flight_status(request, flight_no):

    # Get flight status from the airline operations system
    flight = client.invoke('flights.status', {'flight_no': flight_no})

    # Get gate assignment
    gate = client.invoke('gates.assignment', {'flight_no': flight_no})

    return JsonResponse({
        'flight_no': flight_no,
        'origin': flight['origin'],
        'destination': flight['destination'],
        'status': flight['status'],
        'scheduled_departure': flight['scheduled_departure'],
        'estimated_departure': flight['estimated_departure'],
        'gate': gate['gate_number'],
        'terminal': gate['terminal'],
    })

def get_departures(request):
    terminal = request.GET.get('terminal')

    # Get all departures for the next 4 hours
    departures = client.invoke('flights.departures', {
        'terminal': terminal,
        'hours_ahead': 4,
    })

    return JsonResponse({
        'terminal': terminal,
        'flights': departures['flights'],
    })
```

## Returning HTML {#returning-html}

Views don't have to return JSON - you can return HTML pages just like any other Django view.

```python
# views.py
from django.shortcuts import render
from django_zato import client

def customer_profile(request, customer_id):

    # Get customer data from CRM
    customer = client.invoke('crm.customer.get', {'customer_id': customer_id})

    # Get recent orders
    orders = client.invoke('orders.list', {'customer_id': customer_id, 'limit': 10})

    # Render an HTML template
    return render(request, 'customers/profile.html', {
        'customer': customer,
        'orders': orders['items'],
    })
```

## Next steps {#next-steps_2}

- [Installation](https://zato.io/integrations/django.html#)
- [Invoking services](https://zato.io/integrations/django.html#)
- [Middleware](https://zato.io/integrations/django.html#)

**Middleware**

## Context propagation {#context-propagation}

The plugin includes optional middleware that automatically propagates context from Django requests to Zato services. This is useful for:

- Passing the authenticated Django user to Zato
- Tracking requests with correlation IDs
- Forwarding the client IP address

## Installing the middleware {#installing-the-middleware}

Add the middleware to your Django `settings.py`:

```python
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django_zato.middleware.ZatoMiddleware',  # Add this line
]
```

## What gets propagated {#what-gets-propagated}

When the middleware is installed, every call to `client.invoke` automatically includes these headers:

| Header | Description |
| --- | --- |
| `X-Zato-User` | Username of the authenticated Django user |
| `X-Zato-Correlation-Id` | Unique ID for request tracing (generated if not present) |
| `X-Zato-Forwarded-For` | Client IP address from `REMOTE_ADDR` |

## Accessing context in Zato services {#accessing-context-in-zato-services}

In your Zato services, access the propagated context via `self.request.http.headers`:

```python
from zato.server.service import Service

class MyService(Service):

    def handle(self):
        headers = self.request.http.headers

        django_user = headers.get('x-zato-user', '')
        correlation_id = headers.get('x-zato-correlation-id', '')
        client_ip = headers.get('x-zato-forwarded-for', '')

        self.logger.info('Request from user %s (IP: %s, CID: %s)',
            django_user, client_ip, correlation_id)
```

## Manual context propagation {#manual-context-propagation}

If you don't want to use the middleware, you can pass the Django request explicitly:

```python
from django.http import JsonResponse
from django_zato import client

def my_view(request):
    # Pass the request explicitly for context propagation
    result = client.invoke('my.service', {'key': 'value'}, request=request)
    return JsonResponse(result)
```

## Next steps {#next-steps_3}

- [Installation](https://zato.io/integrations/django.html#)
- [Invoking services](https://zato.io/integrations/django.html#)
- [Examples](https://zato.io/integrations/django.html#)
