ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2022-01-22 14:33:57  阅读:243  来源: 互联网

标签:short 进制 url 转短 Django import original 链接


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 = 0
    idx = 0
    for char in string:
        power = (str_len - (idx + 1))
        num += alphabet.index(char) * (base ** power)
        idx += 1
    return 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

    @classmethod
    def resource(cls):
        return 'short-url'

    @property
    def display(self):
        return {
            'long_url': self.original_url,
            'short_url': self.short_url
        }

    @property
    def resource_display(self):
        return {
            'long_url': self.original_url,
            'short_url': self.short_url,
            'deadline': self.deadline,
            'created_on': self.deadline
        }

    @classmethod
    def model_name(cls):
        return _('ShortUrl')

5. 下面是序列化部分:

# -*- coding: utf-8 -*-
from app.models.short_url import ShortUrl
from rest_framework import serializers


class ShortUrlSerializer(serializers.ModelSerializer):

    class Meta:
        model = ShortUrl
        exclude = ()


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_REQUEST

from 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 = ShortUrl
        fields = {}


class ShortUrlViewSet(viewsets.ModelViewSet, ListDetailMixin):
    """短链接接口"""
    permission_classes = (permissions.IsAdminUser,)
    queryset = ShortUrl.objects.all()
    serializer_class = ShortUrlSerializer

    action_serializers = {
        'retrieve': ShortUrlDetailSerializer,
        'list': ShortUrlSerializer,
    }
    filter_class = ShortUrlFilter
    search_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 = deadline
                obj.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<encode_id>[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经过进制转换得到的字符串,得到短链接之后,在数据库中查询,如果能查得到,取出查询结果的原链接并转发到原链接对应的网址。

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

标签:short,进制,url,转短,Django,import,original,链接
来源: https://blog.csdn.net/weixin_42935779/article/details/122570102

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有