django-link-shortener
Advanced tools
| Metadata-Version: 2.4 | ||
| Name: django-link-shortener | ||
| Version: 1.0.0b0 | ||
| Summary: A simple Django Url Shortener. | ||
| Author-email: Petronald Green <petronaldgreen@gmail.com> | ||
| License-Expression: MIT | ||
| Project-URL: Homepage, https://github.com/ronaldgrn/django-link-shortener | ||
| Keywords: url shortener,link shortener | ||
| Classifier: Development Status :: 5 - Production/Stable | ||
| Classifier: Environment :: Web Environment | ||
| Classifier: Framework :: Django | ||
| Classifier: Framework :: Django :: 3.2 | ||
| Classifier: Framework :: Django :: 4.2 | ||
| Classifier: Framework :: Django :: 5.0 | ||
| Classifier: Framework :: Django :: 5.1 | ||
| Classifier: Framework :: Django :: 5.2 | ||
| Classifier: Intended Audience :: Developers | ||
| Classifier: Operating System :: OS Independent | ||
| Classifier: Programming Language :: Python | ||
| Classifier: Programming Language :: Python :: 3 | ||
| Classifier: Programming Language :: Python :: 3 :: Only | ||
| Classifier: Programming Language :: Python :: 3.8 | ||
| Classifier: Programming Language :: Python :: 3.9 | ||
| Classifier: Programming Language :: Python :: 3.10 | ||
| Classifier: Programming Language :: Python :: 3.11 | ||
| Classifier: Programming Language :: Python :: 3.12 | ||
| Classifier: Programming Language :: Python :: 3.13 | ||
| Classifier: Topic :: Internet :: WWW/HTTP | ||
| Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content | ||
| Requires-Python: >=3.8 | ||
| Description-Content-Type: text/markdown | ||
| License-File: LICENSE | ||
| Requires-Dist: django>=3.2 | ||
| Dynamic: license-file | ||
| # django-link-shortener | ||
| [](https://github.com/ronaldgrn/django-link-shortener/actions/workflows/django-tests.yml) | ||
| [](https://pypi.org/project/django-link-shortener/) | ||
| [](https://pypi.org/project/django-link-shortener/) | ||
| [](https://codecov.io/gh/ronaldgrn/django-link-shortener) | ||
| django-link-shortener is a Django app for creating time-limited and usage-capped short URLs. | ||
| ## Features | ||
| * Generate short links for URLs. | ||
| * Time-sensitive links (configurable lifespan). | ||
| * Usage-limited links (configurable maximum uses). | ||
| * Per-user override of default settings via Django admin. | ||
| * Test endpoint for easy shortcode creation during development. | ||
| * Character set for shortcodes excludes I, i, 1. | ||
| ## Usage | ||
| 1. Install with `pip install django-link-shortener` | ||
| 2. Add `shortener` to your INSTALLED_APPS setting: | ||
| ```python | ||
| INSTALLED_APPS = [ | ||
| ... | ||
| 'shortener', | ||
| ] | ||
| ``` | ||
| 3. Include `shortener.urls` in your project urls.py: | ||
| ```python | ||
| path('s/', include('shortener.urls')), | ||
| ``` | ||
| 4. Run `python manage.py migrate` to create the shortener models. | ||
| 5. Use `shortener.create(user, link)` to generate a shortcode. | ||
| ```python | ||
| from shortener import shortener | ||
| user = User.objects.first() | ||
| shortener.create(user, "https://example.com") | ||
| ``` | ||
| 6. To expand the shortcode use `shortener.expand(shorlink_id)`, | ||
| or visit `http://127.0.0.1:8000/s/<shortcode>/`. | ||
| ## Test Endpoint | ||
| 1. To enable the test endpoint, add the following to settings: | ||
| ```python | ||
| SHORTENER_ENABLE_TEST_PATH = True | ||
| ``` | ||
| 2. Start the development server and visit | ||
| `http://127.0.0.1:8000/s/test/<my-url-here>` to create a test shortcode. | ||
| 3. Visit `http://127.0.0.1:8000/s/<shortcode>/` to be redirected | ||
| ## Configuration Options | ||
| Place in settings.py. Each setting can be overridden on a per-user basis | ||
| using the UrlProfile section in the Django admin. | ||
| **SHORTENER_ENABLED** | ||
| Default: `True` | ||
| Controls whether users without a shortener profile can create shortlinks. | ||
| **SHORTENER_MAX_URLS** | ||
| Default: `-1` | ||
| Controls the default maximum limit of generated urls per account. | ||
| -1 sets infinite. | ||
| **SHORTENER_MAX_CONCURRENT** | ||
| Default: `-1` | ||
| Controls the default maximum limit of *concurrent* (active) | ||
| generated urls per account. -1 sets infinite. | ||
| **SHORTENER_LIFESPAN** | ||
| Default: `-1` | ||
| Sets the default lifespan of links in seconds. -1 sets infinite. | ||
| **SHORTENER_MAX_USES** | ||
| Default: `-1` | ||
| Sets the default amount of times a link can be followed. -1 sets infinite. | ||
| **SHORTENER_LENGTH** | ||
| Default: `5` | ||
| Note: Omitted from UrlProfile. | ||
| Sets how many digits should be used for links. Tries up to three | ||
| times to generate a unique shortcode where Each failure will result | ||
| in length temporarily being increased by 1. | ||
| **SHORTENER_ENABLE_TEST_PATH** | ||
| Default: `False` | ||
| If true, creates shortlinks on authenticated requests to `s/test/<url>/` | ||
| and returns a shortcode. | ||
| ## Common Use Cases | ||
| goo.gl type usage (default). Unlimited concurrent links for an unlimited | ||
| length of time | ||
| ```python | ||
| SHORTENER_ENABLED = True | ||
| SHORTENER_MAX_URLS = -1 | ||
| SHORTENER_MAX_CONCURRENT = -1 | ||
| SHORTENER_LIFESPAN = -1 | ||
| SHORTENER_MAX_USES = -1 | ||
| ``` | ||
| Internal temporary link usage. | ||
| 100 active links with a lifespan of 1 hour. 1 usage per link. | ||
| ```python | ||
| SHORTENER_ENABLED = True | ||
| SHORTENER_MAX_URLS = -1 | ||
| SHORTENER_MAX_CONCURRENT = 100 | ||
| SHORTENER_LIFESPAN = 600 | ||
| SHORTENER_MAX_USES = 1 | ||
| ``` | ||
| ## Changelog | ||
| **v0.5** | ||
| - Replaced NullBooleanField with BooleanField (Credit: sen-den) | ||
| - Replaced travis-ci with github actions | ||
| **v0.4** | ||
| - Allow null values in UrlProfile; null fields will use global values | ||
| - str representation of UrlProfile in admin | ||
| - add user to str representation of UrlMap | ||
| - removed 256 char limit on full_url (Credit: Khaeshah) | ||
| django>=3.2 |
| LICENSE | ||
| MANIFEST.in | ||
| README.md | ||
| pyproject.toml | ||
| django_link_shortener.egg-info/PKG-INFO | ||
| django_link_shortener.egg-info/SOURCES.txt | ||
| django_link_shortener.egg-info/dependency_links.txt | ||
| django_link_shortener.egg-info/requires.txt | ||
| django_link_shortener.egg-info/top_level.txt | ||
| shortener/__init__.py | ||
| shortener/admin.py | ||
| shortener/apps.py | ||
| shortener/models.py | ||
| shortener/shortener.py | ||
| shortener/urls.py | ||
| shortener/views.py | ||
| shortener/migrations/0001_initial.py | ||
| shortener/migrations/0002_auto_20190528_1522.py | ||
| shortener/migrations/0003_auto_20190528_1558.py | ||
| shortener/migrations/0004_alter_urlprofile_enabled.py | ||
| shortener/migrations/__init__.py |
| shortener |
| include LICENSE | ||
| include README.md | ||
| recursive-include shortener/migrations *.py |
+198
| Metadata-Version: 2.4 | ||
| Name: django-link-shortener | ||
| Version: 1.0.0b0 | ||
| Summary: A simple Django Url Shortener. | ||
| Author-email: Petronald Green <petronaldgreen@gmail.com> | ||
| License-Expression: MIT | ||
| Project-URL: Homepage, https://github.com/ronaldgrn/django-link-shortener | ||
| Keywords: url shortener,link shortener | ||
| Classifier: Development Status :: 5 - Production/Stable | ||
| Classifier: Environment :: Web Environment | ||
| Classifier: Framework :: Django | ||
| Classifier: Framework :: Django :: 3.2 | ||
| Classifier: Framework :: Django :: 4.2 | ||
| Classifier: Framework :: Django :: 5.0 | ||
| Classifier: Framework :: Django :: 5.1 | ||
| Classifier: Framework :: Django :: 5.2 | ||
| Classifier: Intended Audience :: Developers | ||
| Classifier: Operating System :: OS Independent | ||
| Classifier: Programming Language :: Python | ||
| Classifier: Programming Language :: Python :: 3 | ||
| Classifier: Programming Language :: Python :: 3 :: Only | ||
| Classifier: Programming Language :: Python :: 3.8 | ||
| Classifier: Programming Language :: Python :: 3.9 | ||
| Classifier: Programming Language :: Python :: 3.10 | ||
| Classifier: Programming Language :: Python :: 3.11 | ||
| Classifier: Programming Language :: Python :: 3.12 | ||
| Classifier: Programming Language :: Python :: 3.13 | ||
| Classifier: Topic :: Internet :: WWW/HTTP | ||
| Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content | ||
| Requires-Python: >=3.8 | ||
| Description-Content-Type: text/markdown | ||
| License-File: LICENSE | ||
| Requires-Dist: django>=3.2 | ||
| Dynamic: license-file | ||
| # django-link-shortener | ||
| [](https://github.com/ronaldgrn/django-link-shortener/actions/workflows/django-tests.yml) | ||
| [](https://pypi.org/project/django-link-shortener/) | ||
| [](https://pypi.org/project/django-link-shortener/) | ||
| [](https://codecov.io/gh/ronaldgrn/django-link-shortener) | ||
| django-link-shortener is a Django app for creating time-limited and usage-capped short URLs. | ||
| ## Features | ||
| * Generate short links for URLs. | ||
| * Time-sensitive links (configurable lifespan). | ||
| * Usage-limited links (configurable maximum uses). | ||
| * Per-user override of default settings via Django admin. | ||
| * Test endpoint for easy shortcode creation during development. | ||
| * Character set for shortcodes excludes I, i, 1. | ||
| ## Usage | ||
| 1. Install with `pip install django-link-shortener` | ||
| 2. Add `shortener` to your INSTALLED_APPS setting: | ||
| ```python | ||
| INSTALLED_APPS = [ | ||
| ... | ||
| 'shortener', | ||
| ] | ||
| ``` | ||
| 3. Include `shortener.urls` in your project urls.py: | ||
| ```python | ||
| path('s/', include('shortener.urls')), | ||
| ``` | ||
| 4. Run `python manage.py migrate` to create the shortener models. | ||
| 5. Use `shortener.create(user, link)` to generate a shortcode. | ||
| ```python | ||
| from shortener import shortener | ||
| user = User.objects.first() | ||
| shortener.create(user, "https://example.com") | ||
| ``` | ||
| 6. To expand the shortcode use `shortener.expand(shorlink_id)`, | ||
| or visit `http://127.0.0.1:8000/s/<shortcode>/`. | ||
| ## Test Endpoint | ||
| 1. To enable the test endpoint, add the following to settings: | ||
| ```python | ||
| SHORTENER_ENABLE_TEST_PATH = True | ||
| ``` | ||
| 2. Start the development server and visit | ||
| `http://127.0.0.1:8000/s/test/<my-url-here>` to create a test shortcode. | ||
| 3. Visit `http://127.0.0.1:8000/s/<shortcode>/` to be redirected | ||
| ## Configuration Options | ||
| Place in settings.py. Each setting can be overridden on a per-user basis | ||
| using the UrlProfile section in the Django admin. | ||
| **SHORTENER_ENABLED** | ||
| Default: `True` | ||
| Controls whether users without a shortener profile can create shortlinks. | ||
| **SHORTENER_MAX_URLS** | ||
| Default: `-1` | ||
| Controls the default maximum limit of generated urls per account. | ||
| -1 sets infinite. | ||
| **SHORTENER_MAX_CONCURRENT** | ||
| Default: `-1` | ||
| Controls the default maximum limit of *concurrent* (active) | ||
| generated urls per account. -1 sets infinite. | ||
| **SHORTENER_LIFESPAN** | ||
| Default: `-1` | ||
| Sets the default lifespan of links in seconds. -1 sets infinite. | ||
| **SHORTENER_MAX_USES** | ||
| Default: `-1` | ||
| Sets the default amount of times a link can be followed. -1 sets infinite. | ||
| **SHORTENER_LENGTH** | ||
| Default: `5` | ||
| Note: Omitted from UrlProfile. | ||
| Sets how many digits should be used for links. Tries up to three | ||
| times to generate a unique shortcode where Each failure will result | ||
| in length temporarily being increased by 1. | ||
| **SHORTENER_ENABLE_TEST_PATH** | ||
| Default: `False` | ||
| If true, creates shortlinks on authenticated requests to `s/test/<url>/` | ||
| and returns a shortcode. | ||
| ## Common Use Cases | ||
| goo.gl type usage (default). Unlimited concurrent links for an unlimited | ||
| length of time | ||
| ```python | ||
| SHORTENER_ENABLED = True | ||
| SHORTENER_MAX_URLS = -1 | ||
| SHORTENER_MAX_CONCURRENT = -1 | ||
| SHORTENER_LIFESPAN = -1 | ||
| SHORTENER_MAX_USES = -1 | ||
| ``` | ||
| Internal temporary link usage. | ||
| 100 active links with a lifespan of 1 hour. 1 usage per link. | ||
| ```python | ||
| SHORTENER_ENABLED = True | ||
| SHORTENER_MAX_URLS = -1 | ||
| SHORTENER_MAX_CONCURRENT = 100 | ||
| SHORTENER_LIFESPAN = 600 | ||
| SHORTENER_MAX_USES = 1 | ||
| ``` | ||
| ## Changelog | ||
| **v0.5** | ||
| - Replaced NullBooleanField with BooleanField (Credit: sen-den) | ||
| - Replaced travis-ci with github actions | ||
| **v0.4** | ||
| - Allow null values in UrlProfile; null fields will use global values | ||
| - str representation of UrlProfile in admin | ||
| - add user to str representation of UrlMap | ||
| - removed 256 char limit on full_url (Credit: Khaeshah) | ||
| [build-system] | ||
| requires = ["setuptools>=69.3"] | ||
| build-backend = "setuptools.build_meta" | ||
| [project] | ||
| name = "django-link-shortener" | ||
| version = "1.0.0-beta" | ||
| dependencies = [ | ||
| "django>=3.2", | ||
| ] | ||
| description = "A simple Django Url Shortener." | ||
| readme = "README.md" | ||
| requires-python = ">= 3.8" | ||
| authors = [ | ||
| {name = "Petronald Green", email = "petronaldgreen@gmail.com"}, | ||
| ] | ||
| keywords = ['url shortener', 'link shortener'] | ||
| license = "MIT" | ||
| license-files = ["LICENSE"] | ||
| classifiers = [ | ||
| "Development Status :: 5 - Production/Stable", | ||
| "Environment :: Web Environment", | ||
| "Framework :: Django", | ||
| "Framework :: Django :: 3.2", | ||
| "Framework :: Django :: 4.2", | ||
| "Framework :: Django :: 5.0", | ||
| "Framework :: Django :: 5.1", | ||
| "Framework :: Django :: 5.2", | ||
| "Intended Audience :: Developers", | ||
| "Operating System :: OS Independent", | ||
| "Programming Language :: Python", | ||
| "Programming Language :: Python :: 3", | ||
| "Programming Language :: Python :: 3 :: Only", | ||
| "Programming Language :: Python :: 3.8", | ||
| "Programming Language :: Python :: 3.9", | ||
| "Programming Language :: Python :: 3.10", | ||
| "Programming Language :: Python :: 3.11", | ||
| "Programming Language :: Python :: 3.12", | ||
| "Programming Language :: Python :: 3.13", | ||
| "Topic :: Internet :: WWW/HTTP", | ||
| "Topic :: Internet :: WWW/HTTP :: Dynamic Content", | ||
| ] | ||
| [project.urls] | ||
| Homepage = "https://github.com/ronaldgrn/django-link-shortener" |
+163
| # django-link-shortener | ||
| [](https://github.com/ronaldgrn/django-link-shortener/actions/workflows/django-tests.yml) | ||
| [](https://pypi.org/project/django-link-shortener/) | ||
| [](https://pypi.org/project/django-link-shortener/) | ||
| [](https://codecov.io/gh/ronaldgrn/django-link-shortener) | ||
| django-link-shortener is a Django app for creating time-limited and usage-capped short URLs. | ||
| ## Features | ||
| * Generate short links for URLs. | ||
| * Time-sensitive links (configurable lifespan). | ||
| * Usage-limited links (configurable maximum uses). | ||
| * Per-user override of default settings via Django admin. | ||
| * Test endpoint for easy shortcode creation during development. | ||
| * Character set for shortcodes excludes I, i, 1. | ||
| ## Usage | ||
| 1. Install with `pip install django-link-shortener` | ||
| 2. Add `shortener` to your INSTALLED_APPS setting: | ||
| ```python | ||
| INSTALLED_APPS = [ | ||
| ... | ||
| 'shortener', | ||
| ] | ||
| ``` | ||
| 3. Include `shortener.urls` in your project urls.py: | ||
| ```python | ||
| path('s/', include('shortener.urls')), | ||
| ``` | ||
| 4. Run `python manage.py migrate` to create the shortener models. | ||
| 5. Use `shortener.create(user, link)` to generate a shortcode. | ||
| ```python | ||
| from shortener import shortener | ||
| user = User.objects.first() | ||
| shortener.create(user, "https://example.com") | ||
| ``` | ||
| 6. To expand the shortcode use `shortener.expand(shorlink_id)`, | ||
| or visit `http://127.0.0.1:8000/s/<shortcode>/`. | ||
| ## Test Endpoint | ||
| 1. To enable the test endpoint, add the following to settings: | ||
| ```python | ||
| SHORTENER_ENABLE_TEST_PATH = True | ||
| ``` | ||
| 2. Start the development server and visit | ||
| `http://127.0.0.1:8000/s/test/<my-url-here>` to create a test shortcode. | ||
| 3. Visit `http://127.0.0.1:8000/s/<shortcode>/` to be redirected | ||
| ## Configuration Options | ||
| Place in settings.py. Each setting can be overridden on a per-user basis | ||
| using the UrlProfile section in the Django admin. | ||
| **SHORTENER_ENABLED** | ||
| Default: `True` | ||
| Controls whether users without a shortener profile can create shortlinks. | ||
| **SHORTENER_MAX_URLS** | ||
| Default: `-1` | ||
| Controls the default maximum limit of generated urls per account. | ||
| -1 sets infinite. | ||
| **SHORTENER_MAX_CONCURRENT** | ||
| Default: `-1` | ||
| Controls the default maximum limit of *concurrent* (active) | ||
| generated urls per account. -1 sets infinite. | ||
| **SHORTENER_LIFESPAN** | ||
| Default: `-1` | ||
| Sets the default lifespan of links in seconds. -1 sets infinite. | ||
| **SHORTENER_MAX_USES** | ||
| Default: `-1` | ||
| Sets the default amount of times a link can be followed. -1 sets infinite. | ||
| **SHORTENER_LENGTH** | ||
| Default: `5` | ||
| Note: Omitted from UrlProfile. | ||
| Sets how many digits should be used for links. Tries up to three | ||
| times to generate a unique shortcode where Each failure will result | ||
| in length temporarily being increased by 1. | ||
| **SHORTENER_ENABLE_TEST_PATH** | ||
| Default: `False` | ||
| If true, creates shortlinks on authenticated requests to `s/test/<url>/` | ||
| and returns a shortcode. | ||
| ## Common Use Cases | ||
| goo.gl type usage (default). Unlimited concurrent links for an unlimited | ||
| length of time | ||
| ```python | ||
| SHORTENER_ENABLED = True | ||
| SHORTENER_MAX_URLS = -1 | ||
| SHORTENER_MAX_CONCURRENT = -1 | ||
| SHORTENER_LIFESPAN = -1 | ||
| SHORTENER_MAX_USES = -1 | ||
| ``` | ||
| Internal temporary link usage. | ||
| 100 active links with a lifespan of 1 hour. 1 usage per link. | ||
| ```python | ||
| SHORTENER_ENABLED = True | ||
| SHORTENER_MAX_URLS = -1 | ||
| SHORTENER_MAX_CONCURRENT = 100 | ||
| SHORTENER_LIFESPAN = 600 | ||
| SHORTENER_MAX_USES = 1 | ||
| ``` | ||
| ## Changelog | ||
| **v0.5** | ||
| - Replaced NullBooleanField with BooleanField (Credit: sen-den) | ||
| - Replaced travis-ci with github actions | ||
| **v0.4** | ||
| - Allow null values in UrlProfile; null fields will use global values | ||
| - str representation of UrlProfile in admin | ||
| - add user to str representation of UrlMap | ||
| - removed 256 char limit on full_url (Credit: Khaeshah) | ||
| [egg_info] | ||
| tag_build = | ||
| tag_date = 0 | ||
+46
-2
| from django.contrib import admin | ||
| from .models import UrlMap, UrlProfile | ||
| class UrlMapAdmin(admin.ModelAdmin): | ||
| model = UrlMap | ||
| raw_id_fields = ( | ||
| "user", | ||
| ) | ||
| list_display = ( | ||
| "short_url", | ||
| "user", | ||
| "usage_count", | ||
| "max_count", | ||
| "lifespan", | ||
| "date_created", | ||
| "date_expired", | ||
| "full_url", | ||
| ) | ||
| search_fields = ( | ||
| "short_url", | ||
| "full_url", | ||
| "user__username", | ||
| ) | ||
| class UrlProfileAdmin(admin.ModelAdmin): | ||
| model = UrlProfile | ||
| raw_id_fields = ( | ||
| "user", | ||
| ) | ||
| list_display = ( | ||
| "user", | ||
| "enabled", | ||
| "max_urls", | ||
| "max_concurrent_urls", | ||
| "default_lifespan", | ||
| "default_max_uses", | ||
| ) | ||
| list_filter = ( | ||
| "enabled", | ||
| ) | ||
| search_fields = ( | ||
| "user__username", | ||
| ) | ||
| # Register your models here. | ||
| admin.site.register(UrlMap) | ||
| admin.site.register(UrlProfile) | ||
| admin.site.register(UrlMap, UrlMapAdmin) | ||
| admin.site.register(UrlProfile, UrlProfileAdmin) |
| from shortener.models import UrlMap, UrlProfile | ||
| from django.conf import settings | ||
| from django.db import IntegrityError | ||
| from datetime import timedelta | ||
| from django.db.models import F | ||
| from datetime import datetime, timedelta | ||
| from django.utils import timezone | ||
@@ -46,3 +48,6 @@ | ||
| else: | ||
| expiry_date = timezone.make_aware(timezone.datetime.max, timezone.get_default_timezone()) | ||
| # Avoid using the absolute maximum date to avoid overflow issues | ||
| # when setting negative timezones | ||
| safe_max_date = datetime(datetime.max.year, 1, 1, tzinfo=timezone.get_default_timezone()) | ||
| expiry_date = safe_max_date | ||
@@ -90,5 +95,11 @@ # Ensure user has not met max_urls quota | ||
| url.usage_count += 1 | ||
| url.save() | ||
| # Avoid a race condition by not using `+= 1` here, and using F() instead: | ||
| url.usage_count = F("usage_count") + 1 | ||
| # Telling `save` to ONLY save changes to the counter. That's all we need | ||
| # to update, and so we'll save a bit of bandwidth by not sending ALL | ||
| # possible values back across the wire, just to update this field: | ||
| # See: https://docs.djangoproject.com/en/5.0/ref/models/instances/#specifying-which-fields-to-save | ||
| url.save(update_fields=["usage_count"]) | ||
| return url.full_url | ||
@@ -5,7 +5,8 @@ from django.urls import path | ||
| app_name = "shortener" | ||
| urlpatterns = [ | ||
| path('<link>/', views.expand), | ||
| path('<link>/', views.expand, name="expand"), | ||
| ] | ||
| if getattr(settings, 'SHORTENER_ENABLE_TEST_PATH', False): | ||
| urlpatterns.append(path('test/<path:link>', views.test)) | ||
| urlpatterns.append(path('test/<path:link>', views.test, name="test")) |
-196
| Metadata-Version: 2.1 | ||
| Name: django-link-shortener | ||
| Version: 0.5 | ||
| Summary: A simple Django Url Shortener. | ||
| Home-page: https://github.com/ronaldgrn/django-link-shortener | ||
| Author: Petronald Green | ||
| Author-email: petronaldgreen@gmail.com | ||
| License: MIT License | ||
| Keywords: url shortener,link shortener | ||
| Platform: UNKNOWN | ||
| Classifier: Development Status :: 5 - Production/Stable | ||
| Classifier: Environment :: Web Environment | ||
| Classifier: Framework :: Django | ||
| Classifier: Framework :: Django :: 2.2 | ||
| Classifier: Framework :: Django :: 3.0 | ||
| Classifier: Framework :: Django :: 3.1 | ||
| Classifier: Framework :: Django :: 3.2 | ||
| Classifier: Framework :: Django :: 4.0 | ||
| Classifier: Intended Audience :: Developers | ||
| Classifier: License :: OSI Approved :: MIT License | ||
| Classifier: Operating System :: OS Independent | ||
| Classifier: Programming Language :: Python | ||
| Classifier: Programming Language :: Python :: 3.6 | ||
| Classifier: Programming Language :: Python :: 3.7 | ||
| Classifier: Programming Language :: Python :: 3.8 | ||
| Classifier: Programming Language :: Python :: 3.9 | ||
| Classifier: Programming Language :: Python :: 3.10 | ||
| Classifier: Topic :: Internet :: WWW/HTTP | ||
| Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content | ||
| License-File: LICENSE | ||
| ===================== | ||
| django-link-shortener | ||
| ===================== | ||
| .. image:: https://github.com/ronaldgrn/django-link-shortener/actions/workflows/django-tests.yml/badge.svg | ||
| :target: https://github.com/ronaldgrn/django-link-shortener/actions/workflows/django-tests.yml | ||
| .. image:: https://img.shields.io/pypi/l/django-link-shortener.svg | ||
| :alt: PyPI - License | ||
| :target: https://pypi.org/project/django-link-shortener/ | ||
| .. image:: https://img.shields.io/pypi/v/django-link-shortener.svg | ||
| :alt: PyPI | ||
| :target: https://pypi.org/project/django-link-shortener/ | ||
| .. image:: https://coveralls.io/repos/github/ronaldgrn/django-link-shortener/badge.svg?branch=master | ||
| :target: https://coveralls.io/github/ronaldgrn/django-link-shortener?branch=master | ||
| django-link-shortener is a simple time and usage sensitive url shortening app. | ||
| Uses A-Za-z0-9 with the exception of I, i and 1. | ||
| Requires user to be logged in for link creation. | ||
| Usage | ||
| ----- | ||
| 1. pip install django-link-shortener | ||
| 2. Add '`shortener'` to your INSTALLED_APPS setting:: | ||
| INSTALLED_APPS = [ | ||
| ... | ||
| 'shortener', | ||
| ] | ||
| 3. Include `shortener.urls` in your project urls.py:: | ||
| path('s/', include('shortener.urls')), | ||
| 4. Run `python manage.py migrate` to create the shortener models. | ||
| Testing | ||
| ------- | ||
| 1. Add the following to settings | ||
| ``` | ||
| SHORTENER_ENABLE_TEST_PATH = True | ||
| ``` | ||
| 1. Start the development server and visit http://127.0.0.1:8000/s/test/<My-URL-HERE> | ||
| to create a test shortcode. | ||
| or | ||
| Use shortener.create(user, link) to generate a link via code. Use shortener.expand(link) | ||
| to revert | ||
| 6. Visit http://127.0.0.1:8000/s/<shortcode>/ to be redirected | ||
| Configuration Options | ||
| --------------------- | ||
| Place in settings.py. Each setting be overridden on a per-user basis using the admin UrlProfile section | ||
| SHORTENER_ENABLED | ||
| Default: True | ||
| Controls whether users without a shortener profile can create shortlinks. | ||
| SHORTENER_MAX_URLS | ||
| Default: -1 | ||
| Controls the default maximum limit of generated urls per account. | ||
| -1 sets infinite. | ||
| SHORTENER_MAX_CONCURRENT | ||
| Default: -1 | ||
| Controls the default maximum limit of *concurrent* (active) generated urls per account. | ||
| -1 sets infinite | ||
| SHORTENER_LIFESPAN | ||
| Default: -1 | ||
| Sets the default lifespan of links in seconds | ||
| -1 sets infinite | ||
| SHORTENER_MAX_USES | ||
| Default: -1 | ||
| Sets the default amount of times a link can be followed | ||
| -1 sets infinite | ||
| SHORTENER_LENGTH | ||
| Default: 5 | ||
| Note: Omitted from UrlProfile | ||
| Sets how many digits should be used for links. | ||
| Tries up to three times to generate a unique shortcode where | ||
| Each failure will result in length temporarily being increased by 1. | ||
| SHORTENER_ENABLE_TEST_PATH | ||
| Default: False | ||
| If true, creates shortlinks for logged in users at s/test/<<url>>/ | ||
| The response is the shortcode to use used at s/<<shortcode>> | ||
| Common Use Cases | ||
| ---------------- | ||
| goo.gl type usage (default). Unlimited concurrent links for an unlimited length of time | ||
| :: | ||
| SHORTENER_ENABLED = True | ||
| SHORTENER_MAX_URLS = -1 | ||
| SHORTENER_MAX_CONCURRENT = -1 | ||
| SHORTENER_LIFESPAN = -1 | ||
| SHORTENER_MAX_USES = -1 | ||
| Internal temporary link usage (such as on nodeferret.com). 100 Temp links per minute. 1 usage per link. | ||
| :: | ||
| SHORTENER_ENABLED = True | ||
| SHORTENER_MAX_URLS = -1 | ||
| SHORTENER_MAX_CONCURRENT = 100 # To prevent spamming | ||
| SHORTENER_LIFESPAN = 600 | ||
| SHORTENER_MAX_USES = 1 | ||
| Changelog | ||
| --------- | ||
| **v0.5** | ||
| - Replaced NullBooleanField with BooleanField (Credit: sen-den) | ||
| - Replaced travis-ci with github actions | ||
| **v0.4** | ||
| - Allow null values in UrlProfile; null fields will use global values | ||
| - str representation of UrlProfile in admin | ||
| - add user to str representation of UrlMap | ||
| - removed 256 char limit on full_url (Credit: Khaeshah) | ||
| Upgrade Instructions | ||
| -------------------- | ||
| **0.3 / 0.4 --> 0.5** | ||
| :: | ||
| pip install django-link-shortener==0.5 | ||
| python manage.py migrate | ||
-18
| shortener/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 | ||
| shortener/admin.py,sha256=4kvFNraIN6h6Lkc8UnlsNcGPWX5WuCHunX_rT7kxE_I,162 | ||
| shortener/apps.py,sha256=AxHfGHS90aZkCOlb0ZWUL4MP94yGDiSgwtKAdtWafMM,93 | ||
| shortener/models.py,sha256=2FHhEz5TtQYEMPQeTqESRsF7GjYSbAeQtTEYztejg8c,1226 | ||
| shortener/shortener.py,sha256=eVD7UuphUBRFWaH4BuaqypNsOf5RXFwcB1KIUUQic2c,3510 | ||
| shortener/tests.py,sha256=NciCsZndnGrA-PJPBLuE0qrOOLtP50S0UUP77jExO1c,7909 | ||
| shortener/urls.py,sha256=A2JsKCazqhXrBOBx9Inq8IovhTpNGa-ii4r-rY9VBVc,265 | ||
| shortener/views.py,sha256=OVcOnp7mCByewsLpt3IL2G6376MUQxovEo7-ZA8sT9I,531 | ||
| shortener/migrations/0001_initial.py,sha256=Fe8Sexd3KlA-SRs-xr2M0kClThEwJwzyvoMHblTjojU,1851 | ||
| shortener/migrations/0002_auto_20190528_1522.py,sha256=XZIBeAqIxKsg1i0ONB-ioV-H8lnsp6CHCl7gxTlQTuo,1145 | ||
| shortener/migrations/0003_auto_20190528_1558.py,sha256=wZHHGCw9tW3r8cMIzb3_KwPm2dSHvnjZygFFyYWm1uU,377 | ||
| shortener/migrations/0004_alter_urlprofile_enabled.py,sha256=sxzE66v3mEmD84rCmdpq-s9oc7oShK6Zt6AtWQcr9Ho,404 | ||
| shortener/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 | ||
| django_link_shortener-0.5.dist-info/LICENSE,sha256=llXuiNIWrY4yw0XfIHUND69bCw52JFPJN-3hECSEKmE,1071 | ||
| django_link_shortener-0.5.dist-info/METADATA,sha256=E_y3EFc7ZszwHC_YDFSz10S1PWJy0WFvdsY5djQqtds,5111 | ||
| django_link_shortener-0.5.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 | ||
| django_link_shortener-0.5.dist-info/top_level.txt,sha256=8fVZ-e_NjbMpSyO0G89mqIAHW-uQ6GeoU9HPaEkWukw,10 | ||
| django_link_shortener-0.5.dist-info/RECORD,, |
| from django.test import TestCase | ||
| from shortener import shortener | ||
| from shortener.models import UrlProfile | ||
| from tests.testapp.models import CustomUser | ||
| import time | ||
| class UrlMapTestCase(TestCase): | ||
| def setUp(self): | ||
| self.bob = CustomUser.objects.create_user('bob', 'bob@bob.com', 'bobpassword') | ||
| self.alice = CustomUser.objects.create_user('alice', 'alice@alice.com', 'alicepassword') | ||
| def test_url_creation(self): | ||
| url = shortener.create(self.bob, "http://devget.net/") | ||
| self.assertEqual(shortener.expand(url), "http://devget.net/") | ||
| def test_invalid_link(self): | ||
| url = shortener.create(self.bob, "http://devget.net/") | ||
| self.assertEqual(shortener.expand(url), "http://devget.net/") # good shortlink | ||
| with self.assertRaisesMessage(KeyError, 'invalid shortlink'): | ||
| self.assertEqual(shortener.expand('photosynthesis'), "http://devget.net/") # bad shortlink | ||
| def test_shortener_enabled_setting(self): | ||
| with self.settings(SHORTENER_ENABLED=False): | ||
| with self.assertRaisesMessage(PermissionError, 'not authorized to create shortlinks'): | ||
| shortener.create(self.bob, "http://devget.net/") | ||
| def test_max_urls_setting(self): | ||
| with self.settings(SHORTENER_MAX_URLS=2): | ||
| shortener.create(self.bob, "http://devget.net/") | ||
| shortener.create(self.bob, "http://devget.net/") | ||
| with self.assertRaisesMessage(PermissionError, 'url quota exceeded'): | ||
| shortener.create(self.bob, "http://devget.net/") | ||
| def test_max_concurrent_setting(self): | ||
| with self.settings(SHORTENER_MAX_CONCURRENT=2): | ||
| shortener.create(self.bob, "http://devget.net/") | ||
| shortener.create(self.bob, "http://devget.net/") | ||
| with self.assertRaisesMessage(PermissionError, 'concurrent quota exceeded'): | ||
| shortener.create(self.bob, "http://devget.net/") | ||
| def test_lifespan_setting(self): | ||
| with self.settings(SHORTENER_LIFESPAN=4): # 4 seconds | ||
| url = shortener.create(self.bob, "http://blog.devget.net/") | ||
| self.assertEqual(shortener.expand(url), "http://blog.devget.net/") | ||
| time.sleep(5) | ||
| with self.assertRaisesMessage(PermissionError, 'shortlink expired'): | ||
| self.assertEqual(shortener.expand(url), "http://blog.devget.net/") | ||
| def test_max_uses_setting(self): | ||
| with self.settings(SHORTENER_MAX_USES=2): | ||
| url = shortener.create(self.bob, "http://blog.devget.net/") | ||
| self.assertEqual(shortener.expand(url), "http://blog.devget.net/") | ||
| self.assertEqual(shortener.expand(url), "http://blog.devget.net/") | ||
| with self.assertRaisesMessage(PermissionError, 'max usages for link reached'): | ||
| # Ensure error is raised when we hit limit | ||
| shortener.expand(url) | ||
| class UrlProfileTestCase(TestCase): | ||
| """ | ||
| Test User overrides | ||
| """ | ||
| def setUp(self): | ||
| self.ronald = CustomUser.objects.create_user('ronald', 'ronald@ronald.com', 'ronaldpassword') | ||
| self.urlProfile, created = UrlProfile.objects.update_or_create( | ||
| user=self.ronald, | ||
| defaults={ | ||
| 'enabled': None, | ||
| 'max_urls': None, | ||
| 'max_concurrent_urls': None, | ||
| 'default_lifespan': None, | ||
| 'default_max_uses': None | ||
| }, | ||
| ) | ||
| # def tearDown(self): | ||
| # # Reset UrlProfile | ||
| # UrlProfile.objects.update_or_create( | ||
| # user=self.ronald, | ||
| # defaults={ | ||
| # 'enabled': None, | ||
| # 'max_urls': None, | ||
| # 'max_concurrent_urls': None, | ||
| # 'default_lifespan': None, | ||
| # 'default_max_uses': None | ||
| # }, | ||
| # ) | ||
| def test_shortener_enabled_setting(self): | ||
| with self.settings(SHORTENER_ENABLED=False): | ||
| with self.assertRaisesMessage(PermissionError, 'not authorized to create shortlinks'): | ||
| shortener.create(self.ronald, "http://devget.net/") | ||
| # Ensure we can override with UrlProfile | ||
| self.urlProfile.enabled = True | ||
| self.urlProfile.save() | ||
| shortener.create(self.ronald, "http://devget.net/") | ||
| # Ensure we can omit with UrlProfile | ||
| self.urlProfile.enabled = None | ||
| self.urlProfile.save() | ||
| with self.assertRaisesMessage(PermissionError, 'not authorized to create shortlinks'): | ||
| shortener.create(self.ronald, "http://devget.net/") | ||
| def test_max_urls_setting(self): | ||
| with self.settings(SHORTENER_MAX_URLS=0): | ||
| with self.assertRaisesMessage(PermissionError, 'url quota exceeded'): | ||
| shortener.create(self.ronald, "http://devget.net/") | ||
| # Ensure we can override with UrlProfile | ||
| self.urlProfile.max_urls = 2 | ||
| self.urlProfile.save() | ||
| shortener.create(self.ronald, "http://devget.net/") | ||
| shortener.create(self.ronald, "http://devget.net/") | ||
| with self.assertRaisesMessage(PermissionError, 'url quota exceeded'): | ||
| shortener.create(self.ronald, "http://devget.net/") | ||
| def test_max_concurrent_setting(self): | ||
| with self.settings(SHORTENER_MAX_CONCURRENT=2): | ||
| shortener.create(self.ronald, "http://devget.net/") | ||
| shortener.create(self.ronald, "http://devget.net/") | ||
| with self.assertRaisesMessage(PermissionError, 'concurrent quota exceeded'): | ||
| shortener.create(self.ronald, "http://devget.net/") | ||
| # Ensure we can override with UrlProfile | ||
| self.urlProfile.max_concurrent_urls = 3 | ||
| self.urlProfile.save() | ||
| shortener.create(self.ronald, "http://devget.net/") | ||
| with self.assertRaisesMessage(PermissionError, 'concurrent quota exceeded'): | ||
| shortener.create(self.ronald, "http://devget.net/") | ||
| def test_lifespan_setting(self): | ||
| with self.settings(SHORTENER_LIFESPAN=2): # 4 seconds | ||
| url = shortener.create(self.ronald, "http://blog.devget.net/") | ||
| self.assertEqual(shortener.expand(url), "http://blog.devget.net/") | ||
| time.sleep(3) | ||
| with self.assertRaisesMessage(PermissionError, 'shortlink expired'): | ||
| self.assertEqual(shortener.expand(url), "http://blog.devget.net/") | ||
| # Ensure we can override with UrlProfile | ||
| self.urlProfile.default_lifespan = 6 | ||
| self.urlProfile.save() | ||
| url = shortener.create(self.ronald, "http://blog.devget.net/") | ||
| self.assertEqual(shortener.expand(url), "http://blog.devget.net/") | ||
| time.sleep(4) | ||
| self.assertEqual(shortener.expand(url), "http://blog.devget.net/") | ||
| time.sleep(3) | ||
| with self.assertRaisesMessage(PermissionError, 'shortlink expired'): | ||
| self.assertEqual(shortener.expand(url), "http://blog.devget.net/") | ||
| def test_max_uses_setting(self): | ||
| with self.settings(SHORTENER_MAX_USES=0): | ||
| url = shortener.create(self.ronald, "http://blog.devget.net/") | ||
| with self.assertRaisesMessage(PermissionError, 'max usages for link reached'): | ||
| # Ensure error is raised when we hit limit | ||
| shortener.expand(url) | ||
| # Ensure we can override with UrlProfile | ||
| self.urlProfile.default_max_uses = 2 | ||
| self.urlProfile.save() | ||
| url = shortener.create(self.ronald, "http://blog.devget.net/") | ||
| self.assertEqual(shortener.expand(url), "http://blog.devget.net/") | ||
| self.assertEqual(shortener.expand(url), "http://blog.devget.net/") | ||
| with self.assertRaisesMessage(PermissionError, 'max usages for link reached'): | ||
| # Ensure error is raised when we hit limit | ||
| shortener.expand(url) |
| shortener |
-5
| Wheel-Version: 1.0 | ||
| Generator: bdist_wheel (0.37.1) | ||
| Root-Is-Purelib: true | ||
| Tag: py3-none-any | ||
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
29515
Infinity%23
Infinity%274
Infinity%