Prepare for bulk destinations redirect
This commit is contained in:
parent
48e034a8e9
commit
6a8f1e1032
8 changed files with 119 additions and 47 deletions
|
@ -1,8 +1,7 @@
|
||||||
# litl
|
# litl
|
||||||
|
|
||||||
The world: Here is a free open source full-featured self-hosted URL shortener.
|
The World: Here is a free open source full-featured self-hosted URL shortener.
|
||||||
|
Grizzly: I can do better!
|
||||||
Grizzly: *choke on PHP* I can do better!
|
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,14 @@ from crispy_forms.helper import FormHelper
|
||||||
from crispy_forms.layout import Submit
|
from crispy_forms.layout import Submit
|
||||||
from .models import Slug
|
from .models import Slug
|
||||||
|
|
||||||
class SlugAddForm(forms.ModelForm):
|
class SlugAddForm(forms.Form):
|
||||||
class Meta:
|
|
||||||
model = Slug
|
destination = forms.CharField(label="Destination", max_length=300,required=True)
|
||||||
fields = ('destination','slug')
|
slug = forms.CharField(label="Slug", max_length=20,required=False)
|
||||||
|
bulk = forms.BooleanField(label="Bulk", required=False)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args,**kwargs)
|
super(SlugAddForm, self).__init__(*args, **kwargs)
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
self.helper.form_method = 'post'
|
self.helper.add_input(Submit('submit', 'Shorten', css_class='btn-primary'))
|
||||||
self.helper.add_input(Submit('submit','Shorten'))
|
self.helper.form_method = 'POST'
|
||||||
|
|
31
litl/migrations/0004_auto_20210101_2227.py
Normal file
31
litl/migrations/0004_auto_20210101_2227.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# Generated by Django 3.1.4 on 2021-01-01 22:27
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('litl', '0003_auto_20210101_1203'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='slug',
|
||||||
|
name='destination',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='slug',
|
||||||
|
name='bulk',
|
||||||
|
field=models.BooleanField(default=False, help_text='Generate a short URL leading to multiple destinations'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Destination',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('destination', models.CharField(help_text='The destination URL, limited to 300 characters.', max_length=300)),
|
||||||
|
('slug', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='litl.slug')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
|
@ -7,17 +7,21 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
class Slug(models.Model):
|
class Slug(models.Model):
|
||||||
|
"""Model of a slug. The slug represent the URL that leads to litl.*.*/slug
|
||||||
|
A slug has one user and can have from one to many destination.
|
||||||
|
"""
|
||||||
|
|
||||||
slug = models.SlugField(
|
slug = models.SlugField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
null=False,
|
null=False,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="Slug of the new URL. Up to 20 caracters including letters, number, underscores and hyphens.", )
|
help_text="Slug of the new URL. Up to 20 caracters including letters, number, underscores and hyphens.", )
|
||||||
destination = models.CharField(
|
|
||||||
max_length=300,
|
bulk = models.BooleanField(
|
||||||
null=False,
|
default=False,
|
||||||
blank=False,
|
help_text="Generate a short URL leading to multiple destinations",
|
||||||
help_text="The destination URL, limited to 300 characters.",)
|
)
|
||||||
|
|
||||||
uid = models.CharField(
|
uid = models.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
null=True,)
|
null=True,)
|
||||||
|
@ -25,6 +29,17 @@ class Slug(models.Model):
|
||||||
auto_now=True,)
|
auto_now=True,)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{} -> {:.15}...'.format(self.slug,self.destination)
|
return '{} -> ?'.format(self.slug)
|
||||||
|
|
||||||
|
class Destination(models.Model):
|
||||||
|
"""Model of a destination. A destination is the end URL wanted by the user.
|
||||||
|
One Slug can have multiple destinations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
slug = models.ForeignKey(Slug, on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
destination = models.CharField(
|
||||||
|
max_length=300,
|
||||||
|
null=False,
|
||||||
|
blank=False,
|
||||||
|
help_text="The destination URL, limited to 300 characters.",)
|
||||||
|
|
|
@ -3,5 +3,19 @@
|
||||||
{% load crispy_forms_tags %}
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
{% if form.errors %}
|
||||||
|
{% for field in form %}
|
||||||
|
{% for error in field.errors %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<strong>{{ error|escape }}</strong>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for error in form.non_field_errors %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<strong>{{ error|escape }}</strong>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
{% crispy form %}
|
{% crispy form %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -93,7 +93,7 @@ Code</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container text-center">
|
<div class="container text-center">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h2>{{slug.destination}}</h2>
|
<h2>{{destinations.first.destination}}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2><p class="fa fa-arrow-down" style="color:grey;"></p></h2>
|
<h2><p class="fa fa-arrow-down" style="color:grey;"></p></h2>
|
||||||
|
|
|
@ -9,7 +9,7 @@ from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
|
||||||
from .settings import TEMPLATES, RANDOM_SLUG_LEN, UID_LEN
|
from .settings import TEMPLATES, RANDOM_SLUG_LEN, UID_LEN
|
||||||
from .models import Slug
|
from .models import Slug, Destination
|
||||||
from .forms import SlugAddForm
|
from .forms import SlugAddForm
|
||||||
|
|
||||||
import random, string
|
import random, string
|
||||||
|
@ -25,8 +25,9 @@ def index(request):
|
||||||
def AddSlug(request):
|
def AddSlug(request):
|
||||||
"""Display the form for creating a new slug.
|
"""Display the form for creating a new slug.
|
||||||
"""
|
"""
|
||||||
form = SlugAddForm(request.POST or None)
|
if request.method == 'POST':
|
||||||
if request.method == 'POST' and form.is_valid():
|
form = SlugAddForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
# Add slug if needed
|
# Add slug if needed
|
||||||
if form.cleaned_data['slug'] == '':
|
if form.cleaned_data['slug'] == '':
|
||||||
rand_slug = ''.join(random.choice(ensemble) for _ in range(RANDOM_SLUG_LEN))
|
rand_slug = ''.join(random.choice(ensemble) for _ in range(RANDOM_SLUG_LEN))
|
||||||
|
@ -44,20 +45,30 @@ def AddSlug(request):
|
||||||
while Slug.objects.filter(uid = rand_uid).exists():
|
while Slug.objects.filter(uid = rand_uid).exists():
|
||||||
rand_uid = ''.join(random.choice(ensemble) for _ in range(UID_LEN))
|
rand_uid = ''.join(random.choice(ensemble) for _ in range(UID_LEN))
|
||||||
|
|
||||||
slug = form.save(commit=False)
|
slug = Slug(slug = form.cleaned_data['slug'],
|
||||||
slug.uid = rand_uid
|
uid = rand_uid,
|
||||||
|
bulk = form.cleaned_data['bulk'],)
|
||||||
slug.save()
|
slug.save()
|
||||||
|
|
||||||
|
destination = Destination(slug = slug,
|
||||||
|
destination = form.cleaned_data['destination'],)
|
||||||
|
destination.save()
|
||||||
|
|
||||||
return HttpResponseRedirect('display/{}'.format(slug.slug))
|
return HttpResponseRedirect('display/{}'.format(slug.slug))
|
||||||
|
else:
|
||||||
|
form = SlugAddForm()
|
||||||
|
|
||||||
# First call, return empty form
|
# First call, return empty form
|
||||||
return render(request,'add_slug.html',{'form':form})
|
return render(request,'add_slug.html',{'form':form})
|
||||||
|
|
||||||
def display(request, slug_got):
|
def display(request, slug_got):
|
||||||
slug = Slug.objects.filter(slug = slug_got).all()
|
slug = Slug.objects.filter(slug = slug_got).all()
|
||||||
|
|
||||||
if slug.count()!=1:
|
if slug.count()!=1:
|
||||||
return render(request,'unknown.html',{'slug':slug_got})
|
return render(request,'unknown.html',{'slug':slug_got})
|
||||||
else:
|
else:
|
||||||
context = {'slug':slug.get(), 'request': request}
|
destinations = Destination.objects.filter(slug=slug.get()).all()
|
||||||
|
context = {'slug':slug.get(), 'destinations':destinations, 'request': request}
|
||||||
return render(request,'display.html',context)
|
return render(request,'display.html',context)
|
||||||
|
|
||||||
def redirect(request,slug_got):
|
def redirect(request,slug_got):
|
||||||
|
@ -66,5 +77,6 @@ def redirect(request,slug_got):
|
||||||
context = {'slug':slug_got,'request': request}
|
context = {'slug':slug_got,'request': request}
|
||||||
return render(request,'unknown.html',context)
|
return render(request,'unknown.html',context)
|
||||||
else:
|
else:
|
||||||
return HttpResponsePermanentRedirect(slug.get().destination)
|
destinations = Destination.objects.filter(slug=slug.get()).all()
|
||||||
|
return HttpResponsePermanentRedirect(destinations.first().destination)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue