# Django UTM Tracker
Django app for extracting and storing UTM tracking values.
## Django support
This package support Django 3.2+, and Python 3.8+
## Background
This app has been designed to integrate the standard `utm_*` querystring
parameters that are used by online advertisers with your Django project.
It does _not_ replace analytics (e.g. Google Analytics) and Adwords tracking,
but does have one crucial difference - it allows you to assign a specific user
to a campaign advert.
This may be useful if you are trying to assess the value of multiple channels /
campaigns.
### Supported querystring parameters
Parameter | Definition
:-- | :--
utm_medium | Identifies what type of link was used.
utm_source | Identifies which site sent the traffic, and is a required parameter.
utm_campaign | Identifies a specific product promotion or strategic campaign.
utm_term | Identifies search terms.
gclid | Identifies a google click, is used for ad tracking in Google Analytics via Google Ads.
aclk | Identifies a Microsoft Ad click (bing), is used for ad tracking.
msclkid | Identifies a Microsoft Ad click (MS ad network), is used for ad tracking.
twclid | Identifies a Twitter Ad click, is used for ad tracking.
fbclid | Identifies a Facebook Ad click, is used for ad tracking.
In addition to the fixed list above, you can also specify custom tags
using the `UTM_TRACKER_CUSTOM_TAGS` setting. Any querystring params that
match these tags are stashed in a JSONField called `custom_tags`.
## How it works
The app works as a pair of middleware classes, that extract `utm_`
values from any incoming request querystring, and then store those
parameters against the request.user (if authenticated), or in the
request.session (if not).
The following shows this workflow (pseudocode - see
`test_utm_and_lead_source` for a real example):
```python
client = Client()
# first request stashes values, but does not create a LeadSource as user is anonymous
client.get("/?utm_medium=medium&utm_source=source...")
assert utm_values_in_session
assert LeadSource.objects.count() == 0
# subsequent request, with authenticated user, extracts values and stores LeadSource
user = User.objects.create(username="fred")
client.force_login(user, backend=settings.FORCED_AUTH_BACKEND)
client.get("/")
assert not utm_values_in_session
assert LeadSource.objects.count() == 1
```
### Why split the middleware in two?
By splitting the middleware into two classes, we enable the use case where we
can track leads without `utm_` querystring parameters. For instance, if you have
an internal referral program, using a simple token, you can capture this as a
`LeadSource` by adding sentinel values to the `request.session`:
```python
def referral(request, token):
# do token handling
...
# medium and source are mandatory for lead source capture
request.session["utm_medium"] = "referral"
request.session["utm_source"] = "internal"
# campaign, term and content are optional fields
request.session["utm_campaign"] = "july"
request.session["utm_term"] = token
request.session["utm_content"] = "buy-me"
return render(request, "landing_page.html")
```
## Configuration
Add the app to `INSTALLED_APPS`:
```python
# settings.py
INSTALLED_APPS = [
...
"utm_tracker"
]
UTM_TRACKER_CUSTOM_TAGS = ["tag1", "tag2"]
```
and add both middleware classes to `MIDDLEWARE`:
```python
# settings.py
MIDDLEWARE = [
...
"utm_tracker.middleware.UtmSessionMiddleware",
"utm_tracker.middleware.LeadSourceMiddleware",
]
```
The `UtmSession` middleware must come before `LeadSource` middleware.
Raw data
{
"_id": null,
"home_page": "https://github.com/yunojuno/django-utm-tracker",
"name": "django-utm-tracker",
"maintainer": "YunoJuno",
"docs_url": null,
"requires_python": ">=3.8,<4.0",
"maintainer_email": "code@yunojuno.com",
"keywords": "",
"author": "YunoJuno",
"author_email": "code@yunojuno.com",
"download_url": "https://files.pythonhosted.org/packages/84/08/e5485977d5023587d65c93eee82a2ab104d43c8f8ce6466c31a91619a543/django_utm_tracker-1.3.2.tar.gz",
"platform": null,
"description": "# Django UTM Tracker\n\nDjango app for extracting and storing UTM tracking values.\n\n## Django support\n\nThis package support Django 3.2+, and Python 3.8+\n\n## Background\n\nThis app has been designed to integrate the standard `utm_*` querystring\nparameters that are used by online advertisers with your Django project.\n\nIt does _not_ replace analytics (e.g. Google Analytics) and Adwords tracking,\nbut does have one crucial difference - it allows you to assign a specific user\nto a campaign advert.\n\nThis may be useful if you are trying to assess the value of multiple channels /\ncampaigns.\n\n### Supported querystring parameters\n\nParameter | Definition\n:-- | :--\nutm_medium | Identifies what type of link was used.\nutm_source | Identifies which site sent the traffic, and is a required parameter.\nutm_campaign | Identifies a specific product promotion or strategic campaign.\nutm_term | Identifies search terms.\ngclid | Identifies a google click, is used for ad tracking in Google Analytics via Google Ads.\naclk | Identifies a Microsoft Ad click (bing), is used for ad tracking.\nmsclkid | Identifies a Microsoft Ad click (MS ad network), is used for ad tracking.\ntwclid | Identifies a Twitter Ad click, is used for ad tracking.\nfbclid | Identifies a Facebook Ad click, is used for ad tracking.\n\nIn addition to the fixed list above, you can also specify custom tags\nusing the `UTM_TRACKER_CUSTOM_TAGS` setting. Any querystring params that\nmatch these tags are stashed in a JSONField called `custom_tags`.\n\n## How it works\n\nThe app works as a pair of middleware classes, that extract `utm_`\nvalues from any incoming request querystring, and then store those\nparameters against the request.user (if authenticated), or in the\nrequest.session (if not).\n\nThe following shows this workflow (pseudocode - see\n`test_utm_and_lead_source` for a real example):\n\n```python\nclient = Client()\n# first request stashes values, but does not create a LeadSource as user is anonymous\nclient.get(\"/?utm_medium=medium&utm_source=source...\")\nassert utm_values_in_session\nassert LeadSource.objects.count() == 0\n\n# subsequent request, with authenticated user, extracts values and stores LeadSource\nuser = User.objects.create(username=\"fred\")\nclient.force_login(user, backend=settings.FORCED_AUTH_BACKEND)\nclient.get(\"/\")\nassert not utm_values_in_session\nassert LeadSource.objects.count() == 1\n```\n\n### Why split the middleware in two?\n\nBy splitting the middleware into two classes, we enable the use case where we\ncan track leads without `utm_` querystring parameters. For instance, if you have\nan internal referral program, using a simple token, you can capture this as a\n`LeadSource` by adding sentinel values to the `request.session`:\n\n```python\ndef referral(request, token):\n # do token handling\n ...\n # medium and source are mandatory for lead source capture\n request.session[\"utm_medium\"] = \"referral\"\n request.session[\"utm_source\"] = \"internal\"\n # campaign, term and content are optional fields\n request.session[\"utm_campaign\"] = \"july\"\n request.session[\"utm_term\"] = token\n request.session[\"utm_content\"] = \"buy-me\"\n return render(request, \"landing_page.html\")\n```\n\n## Configuration\n\nAdd the app to `INSTALLED_APPS`:\n\n```python\n# settings.py\nINSTALLED_APPS = [\n ...\n \"utm_tracker\"\n]\n\nUTM_TRACKER_CUSTOM_TAGS = [\"tag1\", \"tag2\"]\n```\n\nand add both middleware classes to `MIDDLEWARE`:\n\n```python\n# settings.py\nMIDDLEWARE = [\n ...\n \"utm_tracker.middleware.UtmSessionMiddleware\",\n \"utm_tracker.middleware.LeadSourceMiddleware\",\n]\n```\n\nThe `UtmSession` middleware must come before `LeadSource` middleware.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Django app for extracting and storing UTM tracking values.",
"version": "1.3.2",
"project_urls": {
"Documentation": "https://github.com/yunojuno/django-utm-tracker",
"Homepage": "https://github.com/yunojuno/django-utm-tracker",
"Repository": "https://github.com/yunojuno/django-utm-tracker"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "0332b9cd7d1b562483767629ed174d06c44362e1d63e899d074304c2a1c30612",
"md5": "38c3841a0c63ff5ed310e9acb450d0c1",
"sha256": "014df7b1d2cd6ceac2bb6524c34eeb78985247b3bd5eaee78f5a0e97efab31af"
},
"downloads": -1,
"filename": "django_utm_tracker-1.3.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "38c3841a0c63ff5ed310e9acb450d0c1",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8,<4.0",
"size": 15648,
"upload_time": "2023-11-11T14:12:26",
"upload_time_iso_8601": "2023-11-11T14:12:26.857577Z",
"url": "https://files.pythonhosted.org/packages/03/32/b9cd7d1b562483767629ed174d06c44362e1d63e899d074304c2a1c30612/django_utm_tracker-1.3.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "8408e5485977d5023587d65c93eee82a2ab104d43c8f8ce6466c31a91619a543",
"md5": "edb7586167573b6bd5c3c2de5446f0bb",
"sha256": "e35b4a5e37b5ae52a2f1e13759173e2ed27a892ec64f9fbdb01d3edd8bc61bf6"
},
"downloads": -1,
"filename": "django_utm_tracker-1.3.2.tar.gz",
"has_sig": false,
"md5_digest": "edb7586167573b6bd5c3c2de5446f0bb",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8,<4.0",
"size": 10613,
"upload_time": "2023-11-11T14:12:28",
"upload_time_iso_8601": "2023-11-11T14:12:28.611043Z",
"url": "https://files.pythonhosted.org/packages/84/08/e5485977d5023587d65c93eee82a2ab104d43c8f8ce6466c31a91619a543/django_utm_tracker-1.3.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-11-11 14:12:28",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "yunojuno",
"github_project": "django-utm-tracker",
"travis_ci": false,
"coveralls": true,
"github_actions": true,
"tox": true,
"lcname": "django-utm-tracker"
}