We will build a simple Url Shortener using django and redis, data will be stored in SQLite and redis will be used as cache storage.
Setup a virtual environment for this project
Setup a virtual environment for this project, so it will not affect the projects in your machine.
Installing the dependency for the project
pip install django
pip install redis
above command will install the latest version of the packages
Setting up the django project
for detailed reference about django visit the page
Now we can create our project
django-admin startproject urlshortener
cd urlshortener
To check the app running successfully, run this it will open a rocket page(in terminal you can see unapplied migration, we will see this in migrate)
python manage.py runserver
The below command will create a app call short(where logic will be written) inside the urlshortener
python manage.py startapp short
now we have to add the app short to the settings.py, this will help django to identify the changes in models
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'short',
]
What is django model?
You can think model as a table in django, in SQL we use tables to store the data, right, In the same way model is being used. Table schema can be created in model then it can be migrated to the database.
by default django shipped with SQLite DB, you can change it to whatever data source you want to connect
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
models
Now add this code in short/models.py
from django.db import models
class URL(models.Model):
full_url = models.TextField()
hash = models.TextField(unique=True)
class Meta:
db_table = 'url_shortener'
in full_url we will hold the given url
in hash will store the unique key
db_table holds the name of the table
Run this
python manage.py makemigrations short
By running makemigrations, you’re telling Django that you’ve made some changes to your models, the above command only looks for the changes in short app, to load changes from all the app models just run this
python manage.py makemigrations
this will creates migrations files inside the story/migrations directory, after that to move the change to SQLite db run this
python manage.py migrate
when you run this command this will apply the admin, session related migration to the database, all the warnings in the terminal will be gone
View and Urls
Where we will write the logic
create a urls.py inside short directory, after that include the file in urlshortener/urls.py, just like this
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include("short.urls")),
]
now lets write a simple HttpResponse view called as home, in short/views.py
from django.http import HttpResponse
def home(request):
current_site = get_current_site(request)
return HttpResponse("<h1>Url Shortener</h1>")
then link the view with a url
from django.urls import path
from short import views
urlpatterns = [
path('', views.home, name="Home"),
]
open the home url (http://127.0.0.1:8000/) you will get the HttpResponse
project is working fine, now lets write some logic for the shortener
for detailed reference check this Designing a URL Shortening service like TinyURL
this will generate a random key value of length 7 with the combination of uppercase, lowercase and numbers. N can be adjusted as we want
import string, random
# hash length
N = 7
s = string.ascii_uppercase + string.ascii_lowercase + string.digits
# generate a random string of length 7
url_id = ''.join(random.choices(s, k=N))
convert the logic to a function and it will create a new record in table for each new url short request
import string, random
from short.models import URL
def shortit(long_url):
# hash length
N = 7
s = string.ascii_uppercase + string.ascii_lowercase + string.digits
# generate a random string of length 7
url_id = ''.join(random.choices(s, k=N))
# check the hash id is in
if not URL.objects.filter(hash=url_id).exists():
# create a new entry for new one
create = URL.objects.create(full_url=long_url, hash=url_id)
return url_id
else:
# if hash id already exists create a new hash
shortit(url)
this view function will get the request and use the above function to convert it
from django.http import JsonResponse
from django.contrib.sites.shortcuts import get_current_site
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def short_url(request):
long_url = request.POST.get("url")
# get the unique id for long url
hash = shortit(long_url)
# get the host url
current_site = get_current_site(request)
data = {
"success": True,
"id": hash,
"link": "http://{}/{}".format(current_site, hash),
"long_url": long_url
}
return JsonResponse(data)
link this view function to short/urls.py urlpatterns list
path('shorten', views.short_url, name="ShortUrl"),
Now lets do the retrieving logic
redis - in-memory data structure store
we will use redis as a cache storage, I'm using redis in my local system
import redis
# host - running host
# port - the port in which its running
# db - by default 0 is the default db, you can change it
rds = redis.Redis(host='localhost', port=6379, db=0)
just run it using redis-server, once the redis server is stoped data will be cleared.
from django.shortcuts import redirect
def redirector(request,hash_id=None):
# get the value from redis key, if value not in return None
hash_code = rds.get(hash_id)
if hash_code is not None:
return redirect(hash_code.decode('ascii'))
if URL.objects.filter(hash=hash_id).exists():
url = URL.objects.get(hash=hash_id)
# set the value in redis for faster access
rds.set(hash_id,url.full_url)
# redirect the page to the respective url
return redirect(url.full_url)
else:
# if the give key not in redis and db
return JsonResponse({"success":False})
Note: redis in python 3 always return binary string for that we used decode('ascii')
link the redirector view function in short/urls.py urlpatterns list
path('<str:hash_id>/', views.redirector, name="Redirector"),
hash_id is a URL parameters used to get the value from url
DONE RIGHT, this is the project structure
Thanks you, have a great day ahead.🤪😎
Top comments (5)
Thanks. It looks very good <3. For improvement;
You can find count for all possibilities to hash length. Because you can check it like that;
So you avoid duplicates. Of course, you should check the hash either exists in the database or not.
Sure, we can use incremental value. Instead of checking the hash is already exists.
This will improve the performance.
Awesome
Cool!
import redis
Import 'redis' could not be solved from resource
can you tell me how to import it, I did pip install redis and then import it but not working