diff --git a/contents/docs/libraries/django.mdx b/contents/docs/libraries/django.mdx index 424f62dcba7f..5a8b054d692f 100644 --- a/contents/docs/libraries/django.mdx +++ b/contents/docs/libraries/django.mdx @@ -5,7 +5,7 @@ platformLogo: django import AgentIntegrationSection from "../components/AgentIntegrationSection.mdx" -PostHog makes it easy to get data about traffic and usage of your Django app. Integrating PostHog enables analytics, custom events capture, feature flags, and more. +PostHog makes it easy to get data about traffic and usage of your Django app. Integrating PostHog enables analytics, custom events capture, feature flags, error tracking, and more. This guide walks you through integrating PostHog into your Django app using the [Python SDK](/docs/libraries/python). @@ -15,41 +15,51 @@ This guide walks you through integrating PostHog into your Django app using the To start, run `pip install posthog` to install PostHog’s Python SDK. -Then, set the PostHog API key and host in your `AppConfig` in your `your_app/apps.py` so that's it's available everywhere: +> **Note:** Version `7.x` of the PostHog Python SDK requires Python 3.10 or higher. + +Then, configure PostHog in your app config so it's initialized when Django starts: ```python file=your_app/apps.py from django.apps import AppConfig import posthog class YourAppConfig(AppConfig): - name = "your_app_name" + name = 'your_app_name' + def ready(self): posthog.api_key = '' posthog.host = '' ``` -You can find your project token and instance address in [your project settings](https://us.posthog.com/project/settings). - -Next, if you haven't done so already, make sure you add your `AppConfig` to your `settings.py` under `INSTALLED_APPS`: +Next, if you haven't done so already, add your `AppConfig` to `INSTALLED_APPS` in `settings.py`: ```python file=settings.py INSTALLED_APPS = [ - # other apps - 'your_app_name.apps.MyAppConfig', # Add your app config + # ... other apps + 'your_app_name.apps.YourAppConfig', ] ``` -Lastly, to access PostHog in any file, simply `import posthog` and call the method you'd like. For example, to capture an event: +You can find your project token and instance address in [your project settings](https://app.posthog.com/project/settings). + +To capture events from any file, import `posthog` and call the method you need. For example: ```python import posthog +from posthog import identify_context def some_request(request): with posthog.new_context(): - posthog.identify_context(request.user.id) + # Django includes request.user for anonymous visitors too. Only identify + # the context when the visitor is logged in. + if request.user.is_authenticated: + identify_context(str(request.user.pk)) + posthog.capture('event_name') ``` +Events captured without a context or explicit `distinct_id` are sent as [anonymous events](/docs/data/anonymous-vs-identified-events) with an auto-generated `distinct_id`. See the [Python SDK docs](/docs/libraries/python#person-profiles-and-properties) for more details. + ## Identifying users import IdentifyBackendCallout from '../_snippets/identify-backend-callout.mdx' diff --git a/contents/docs/libraries/flask.mdx b/contents/docs/libraries/flask.mdx index 58b079994dbf..80240fb844a2 100644 --- a/contents/docs/libraries/flask.mdx +++ b/contents/docs/libraries/flask.mdx @@ -3,7 +3,7 @@ title: Flask platformLogo: flask --- -PostHog makes it easy to get data about traffic and usage of your Flask app. Integrating PostHog enables analytics, custom events capture, feature flags, and more. +PostHog makes it easy to get data about traffic and usage of your Flask app. Integrating PostHog enables analytics, custom events capture, feature flags, error tracking, and more. This guide walks you through integrating PostHog into your Flask app using the [Python SDK](/docs/libraries/python). @@ -11,29 +11,31 @@ This guide walks you through integrating PostHog into your Flask app using the [ To start, run `pip install posthog` to install PostHog’s Python SDK. +> **Note:** Version `7.x` of the PostHog Python SDK requires Python 3.10 or higher. + Then, initialize PostHog where you'd like to use it. For example, here's how to capture an event in a simple route: ```python file=app.py -package main -from flask import Flask, render_template, request, redirect, session, url_for +from flask import Flask from posthog import Posthog +app = Flask(__name__) + posthog = Posthog( - '', - host='' + '', + host='', ) @app.route('/api/dashboard', methods=['POST']) def api_dashboard(): posthog.capture( - 'dashboard_api_called' + 'dashboard_api_called', distinct_id='distinct_id_of_your_user', ) return '', 204 - ``` -You can find your project token and instance address in [your project settings](https://us.posthog.com/project/settings). +You can find your project token and instance address in [your project settings](https://app.posthog.com/project/settings). ## Identifying users @@ -41,6 +43,36 @@ import IdentifyBackendCallout from '../_snippets/identify-backend-callout.mdx' +## Request contexts + +Use [contexts](/docs/libraries/python#contexts) to share identity, session IDs, and tags across multiple captures during a request: + +```python +from flask import request, session +from posthog import identify_context, set_context_session, tag + +@app.route('/api/dashboard', methods=['POST']) +def api_dashboard(): + with posthog.new_context(): + distinct_id = request.headers.get('X-POSTHOG-DISTINCT-ID') or session.get('user_id') + if distinct_id: + identify_context(str(distinct_id)) + + session_id = request.headers.get('X-POSTHOG-SESSION-ID') + if session_id: + set_context_session(session_id) + + tag('$current_url', request.url) + tag('$request_method', request.method) + tag('$request_path', request.path) + + posthog.capture('dashboard_api_called') + + return '', 204 +``` + +Events captured without a context or explicit `distinct_id` are sent as [anonymous events](/docs/data/anonymous-vs-identified-events) with an auto-generated `distinct_id`. See the [Python SDK docs](/docs/libraries/python#person-profiles-and-properties) for more details. + ## Error tracking import PythonFlaskExceptionAutocapture from './python/_snippets/python-flask-exception-autocapture.mdx' diff --git a/contents/docs/libraries/python/_snippets/django-context-middleware.mdx b/contents/docs/libraries/python/_snippets/django-context-middleware.mdx index 1e19213540e7..4b2c2260bcdc 100644 --- a/contents/docs/libraries/python/_snippets/django-context-middleware.mdx +++ b/contents/docs/libraries/python/_snippets/django-context-middleware.mdx @@ -1,8 +1,8 @@ -The Python SDK provides a Django middleware that automatically wraps all requests with a [context](/docs/libraries/python#contexts). This middleware extracts session and user information from request headers and tags all events captured during the request with relevant metadata. +The Python SDK provides a Django middleware that automatically wraps all requests with a [context](/docs/libraries/python#contexts). This middleware extracts session and user information from each request and tags all events captured during that request with relevant metadata. ### Basic setup -Add the middleware to your Django settings: +Add the middleware to your Django settings. If your app uses Django authentication, place it after `django.contrib.auth.middleware.AuthenticationMiddleware` so the middleware can use the authenticated Django user as a distinct ID fallback and capture the user's email. ```python MIDDLEWARE = [ @@ -12,14 +12,22 @@ MIDDLEWARE = [ ] ``` +The middleware uses the globally configured `posthog` client by default, so you don't need to create or pass it a separate client instance. + The middleware automatically extracts and uses: - **Session ID** from the `X-POSTHOG-SESSION-ID` header, if present -- **Distinct ID** from the `X-POSTHOG-DISTINCT-ID` header, if present +- **Distinct ID** from the `X-POSTHOG-DISTINCT-ID` header, if present, falling back to the authenticated Django user's `pk` (Django's primary-key alias, which works with custom user models) +- **User email** from the authenticated Django user's `email` as `email` - **Current URL** as `$current_url` - **Request method** as `$request_method` +- **Request path** as `$request_path` +- **Forwarded IP address** from `X-Forwarded-For` as `$ip` +- **User agent** from `User-Agent` as `$user_agent` + +The session and distinct ID headers are sanitized before use. Empty values are ignored, control characters are removed, values are trimmed, and values are capped at 1000 characters. -All events captured during the request (including exceptions) will include these properties and be associated with the extracted session and distinct ID. +All events captured during the request (including exceptions) include these properties and are associated with the extracted session and distinct ID. If you are using PostHog on your frontend, the JavaScript Web SDK will add the session and distinct ID headers automatically if you enable tracing headers. @@ -31,7 +39,9 @@ posthog.init('', { ### Exception capture -By default, the middleware captures exceptions and sends them to PostHog's error tracking. Disable this by setting: +By default, the middleware captures exceptions and sends them to PostHog's error tracking using the globally configured `posthog` client. This includes Django view exceptions that Django converts into error responses. + +Disable this by setting: ```python # settings.py @@ -48,7 +58,8 @@ def add_user_tags(request): # type: (HttpRequest) -> Dict[str, Any] tags = {} if hasattr(request, 'user') and request.user.is_authenticated: - tags['user_id'] = request.user.id + # Use pk instead of id so this works with custom User primary keys. + tags['user_id'] = str(request.user.pk) tags['email'] = request.user.email return tags @@ -98,7 +109,8 @@ def add_request_context(request): tags = {} if hasattr(request, 'user') and request.user.is_authenticated: tags['user_type'] = 'authenticated' - tags['user_id'] = str(request.user.id) + # Use pk instead of id so this works with custom User primary keys. + tags['user_id'] = str(request.user.pk) else: tags['user_type'] = 'anonymous' @@ -123,4 +135,6 @@ POSTHOG_MW_TAG_MAP = clean_tags POSTHOG_MW_CAPTURE_EXCEPTIONS = True ``` -All events captured within the request context automatically include the configured tags and are associated with the session and user identified from the request headers. +All events captured within the request context automatically include the configured tags and are associated with the session and user identified from the request headers or Django authentication. + +The middleware supports both sync (WSGI) and async (ASGI) Django applications. In async mode, it uses Django's `request.auser()` API when available to avoid synchronous user access. diff --git a/contents/docs/libraries/python/_snippets/python-flask-exception-autocapture.mdx b/contents/docs/libraries/python/_snippets/python-flask-exception-autocapture.mdx index 944b746d5e4d..a939c4e54594 100644 --- a/contents/docs/libraries/python/_snippets/python-flask-exception-autocapture.mdx +++ b/contents/docs/libraries/python/_snippets/python-flask-exception-autocapture.mdx @@ -2,6 +2,7 @@ from flask import Flask, jsonify from posthog import Posthog +app = Flask(__name__) posthog = Posthog('', host='') @app.errorhandler(Exception)