基于Django+DRF实现长链接转短链接

1. 主要的库:

  • Django == 3.2.9
  • djangorestframework == 3.12.4
  • django-filter == 21.1
  • djangorestframework-filters == 1.0.0.dev2
  • django-extensions == 3.1.5
  • django-rest-framework-rules == 1.0.0
  • mysqlclient == 2.0.3

2. 实现思路:

  1. 通过mysql数据库,建立原链接和短链接的关系并存储起来;
  2. 通过进制转换,把10进制转为62进制,能大大缩短字符串的长度;
  3. 用户访问短链接的时候转发到原来的链接。

3. 进制转换(10进制转62进制):

把数据表自增的id转换成62进制,这样即使数据很多,短链接的长度也能不会太长,下面直接上代码:

# -*- coding: utf-8 -*-__author__ = 'JayChen'BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"def encode(num: int, alphabet: str = BASE62) -> str:"""10进制转62进制Args:num:            10进制的整型数字alphabet:       字符串Return:62进制字符串"""if num == 0:return alphabet[0]arr = []base = len(alphabet)while num:num, rem = divmod(num, base)arr.append(alphabet[rem])arr.reverse()return ''.join(arr)def decode(string: str, alphabet: str = BASE62) -> int:"""62进制转10进制Args:string:         62进制的字符串alphabet:       字符串Return:10进制数字"""base = len(alphabet)str_len = len(string)num = 0idx = 0for char in string:power = (str_len - (idx + 1))num += alphabet.index(char) * (base ** power)idx += 1return num

4. 创建数据库表:

# -*- coding: utf-8 -*-
"""短链接表"""
from django.db import models
from django.conf import settings
from django_extensions.db.fields import CreationDateTimeField
from django.utils.translation import gettext as ___author__ = 'JayChen'class ShortUrl(models.Model):# Translators: 原始链接original_url = models.CharField(_('Original url'), max_length=255, blank=True, null=True, db_index=True)# Translators: 短链接short_url = models.CharField(_('Short url'), max_length=255, blank=True, null=True, db_index=True)# Translators: 有效截止时间deadline = models.DateTimeField(_('Deadline'), blank=True, null=True, db_index=True)# Translators: 创建时间created_on = CreationDateTimeField(_('Created on'), db_index=True)# Translators: 创建人created_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='%(app_label)s_%(class)s_created_by',blank=True, null=True, on_delete=models.SET_NULL)class Meta:ordering = ['-id']app_label = 'app'db_table = 'app_short_url'def __str__(self):return self.short_url@classmethoddef resource(cls):return 'short-url'@propertydef display(self):return {'long_url': self.original_url,'short_url': self.short_url}@propertydef resource_display(self):return {'long_url': self.original_url,'short_url': self.short_url,'deadline': self.deadline,'created_on': self.deadline}@classmethoddef model_name(cls):return _('ShortUrl')

5. 下面是序列化部分:

# -*- coding: utf-8 -*-
from app.models.short_url import ShortUrl
from rest_framework import serializersclass ShortUrlSerializer(serializers.ModelSerializer):class Meta:model = ShortUrlexclude = ()class ShortUrlDetailSerializer(ShortUrlSerializer):class Meta(ShortUrlSerializer.Meta):exclude = ()

6. 接口部分:

# -*- coding: utf-8 -*-
from django.conf import settings
from django.shortcuts import redirect
import rest_framework_filters as filters
from rest_framework import permissions, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.status import HTTP_400_BAD_REQUESTfrom app.models.short_url import ShortUrl
from utils.base62 import encode
from app.serializers.short_url import ShortUrlSerializer, ShortUrlDetailSerializer
from app.viewsets.mixin import ListDetailMixin__author__ = 'JayChen'class ShortUrlFilter(filters.FilterSet):class Meta:model = ShortUrlfields = {}class ShortUrlViewSet(viewsets.ModelViewSet, ListDetailMixin):"""短链接接口"""permission_classes = (permissions.IsAdminUser,)queryset = ShortUrl.objects.all()serializer_class = ShortUrlSerializeraction_serializers = {'retrieve': ShortUrlDetailSerializer,'list': ShortUrlSerializer,}filter_class = ShortUrlFiltersearch_fields = ('short_url',)@action(detail=False, methods=['POST'], url_path='to-short', permission_classes=(permissions.IsAuthenticated,))def to_short(self, request):"""原链接转短链接的接口"""original_url = request.data.get('original_url')deadline = request.data.get('deadline')if original_url:obj, created = ShortUrl.objects.get_or_create(original_url=original_url, created_by=request.user)if created:obj.short_url = f'{settings.DOMAIN}api/v1/{encode(obj.pk)}/render-original/'if deadline:obj.deadline = deadlineobj.save(update_fields=['short_url', 'deadline'])obj.refresh_from_db()_serializer = self.get_serializer_class()return Response(_serializer(obj, many=False).data)raise HTTP_400_BAD_REQUEST@action(detail=False, methods=['GET'], url_path='(?P[0-9a-fA-Z]+)/render-original',permission_classes=(permissions.AllowAny,))def render_original(self, request, encode_id):"""短链接转发原链接的接口"""short_url = f'{settings.DOMAIN}api/v1/{encode_id}/render-original/'obj = ShortUrl.objects.filter(short_url=short_url).first()if obj:return redirect(obj.original_url)raise HTTP_400_BAD_REQUEST

注:settings.DOMAIN指的是在settings.py文件中设置一个叫DOMAIN的字段,值是自己的域名

我在这里自定义了两个接口,to_short()和render_original(),分别是:

  • to_short():原链接转短链接的接口
  • render_original():短链接转发原链接的接口
to_short()方法详解:

接收用户发送过来的两个数据:original_url和deadline ,即:原链接和失效时间,通过get_or_create()方法,先查询数据库表中没有此用户的这个原链接,如果没有,就新建一条数据,对新建数据的id进制转换得到短链接并保存,如果数据库有相关的数据,就只需要更新失效时间,没有失效时间就不用更新了,最后序列化对象返回数据。

render_original()方法详解:

从用户访问的短链接里面获得encode_id,这个是数据库表自增的id经过进制转换得到的字符串,得到短链接之后,在数据库中查询,如果能查得到,取出查询结果的原链接并转发到原链接对应的网址。

注:功能还没完善,例如短链接过期失效的处理,作者最近年末没时间去折腾,有兴趣的读者可以自己完善哈!


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部