added more common models, added short link base model, updated shortener

This commit is contained in:
Alexander Karpov 2023-03-15 22:32:33 +03:00
parent 39ef0fb06b
commit 9b6934288c
24 changed files with 354 additions and 32 deletions

View File

@ -0,0 +1,24 @@
# Generated by Django 4.1.7 on 2023-03-15 12:38
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("shortener", "0001_initial"),
("blog", "0002_alter_comment_options"),
]
operations = [
migrations.AddField(
model_name="post",
name="short_link",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="shortener.link",
),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 4.1.7 on 2023-03-15 12:39
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("shortener", "0001_initial"),
("blog", "0003_post_short_link"),
]
operations = [
migrations.AlterField(
model_name="post",
name="short_link",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="shortener.link",
),
),
]

View File

@ -1,15 +1,16 @@
from ckeditor_uploader.fields import RichTextUploadingField
from colorfield.fields import ColorField
from django.db import models
from django.db.models import Count, ImageField, SlugField
from django.db.models import Count, SlugField
from django.urls import reverse
from akarpov.common.models import BaseImageModel
from akarpov.tools.shortener.models import ShortLink
from akarpov.users.models import User
from akarpov.utils.files import user_file_upload_mixin
from akarpov.utils.string import cleanhtml
class Post(models.Model):
class Post(BaseImageModel, ShortLink):
title = models.CharField(max_length=100, blank=False)
body = RichTextUploadingField(blank=False)
@ -25,9 +26,6 @@ class Post(models.Model):
created = models.DateTimeField(auto_now_add=True)
edited = models.DateTimeField(auto_now=True)
image = ImageField(upload_to=user_file_upload_mixin, blank=True)
image_cropped = ImageField(upload_to="cropped/", blank=True)
tags = models.ManyToManyField("blog.Tag", related_name="posts")
def __str__(self):

View File

@ -0,0 +1,11 @@
from django.db import models
from akarpov.utils.files import user_file_upload_mixin
class BaseImageModel(models.Model):
image = models.ImageField(upload_to=user_file_upload_mixin, blank=True)
image_cropped = models.ImageField(upload_to="cropped/", blank=True)
class Meta:
abstract = True

View File

@ -0,0 +1,33 @@
# Generated by Django 4.1.7 on 2023-03-15 12:38
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("shortener", "0001_initial"),
("files", "0002_alter_basefile_options_alter_folder_options_and_more"),
]
operations = [
migrations.AddField(
model_name="basefile",
name="short_link",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="shortener.link",
),
),
migrations.AddField(
model_name="folder",
name="short_link",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="shortener.link",
),
),
]

View File

@ -0,0 +1,35 @@
# Generated by Django 4.1.7 on 2023-03-15 12:39
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("shortener", "0001_initial"),
("files", "0003_basefile_short_link_folder_short_link"),
]
operations = [
migrations.AlterField(
model_name="basefile",
name="short_link",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="shortener.link",
),
),
migrations.AlterField(
model_name="folder",
name="short_link",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="shortener.link",
),
),
]

View File

@ -7,12 +7,14 @@
SlugField,
TextField,
)
from django.urls import reverse
from model_utils.models import TimeStampedModel
from akarpov.tools.shortener.models import ShortLink
from akarpov.utils.files import user_file_upload_mixin
class BaseFile(TimeStampedModel):
class BaseFile(TimeStampedModel, ShortLink):
"""model to store user's files"""
name = CharField(max_length=100)
@ -28,16 +30,22 @@ class BaseFile(TimeStampedModel):
file = FileField(blank=False, upload_to=user_file_upload_mixin)
def get_absolute_url(self):
return reverse("files:view", kwargs={"slug": self.slug})
def __str__(self):
return f"file: {self.name}"
class Folder(TimeStampedModel):
class Folder(TimeStampedModel, ShortLink):
name = CharField(max_length=100)
slug = SlugField(max_length=20, blank=True)
user = ForeignKey("users.User", related_name="files_folders", on_delete=CASCADE)
parent = ForeignKey("self", related_name="children", on_delete=CASCADE)
def get_absolute_url(self):
return reverse("files:folder", kwargs={"slug": self.slug})
def __str__(self):
return f"file: {self.name}"

9
akarpov/files/urls.py Normal file
View File

@ -0,0 +1,9 @@
from django.urls import path
from akarpov.files.views import files_view, folder_view
app_name = "files"
urlpatterns = [
path("<str:slug>", files_view, name="view"),
path("f/<str:slug>", folder_view, name="folder"),
]

View File

@ -0,0 +1,21 @@
from django.views.generic import DetailView
from akarpov.files.models import BaseFile, Folder
class FileView(DetailView):
template_name = "files/view.html"
model = BaseFile
slug_field = "slug"
files_view = FileView.as_view()
class FileFolderView(DetailView):
template_name = "files/folder.html"
model = Folder
slug_field = "slug"
folder_view = FileFolderView.as_view()

View File

@ -0,0 +1,27 @@
# Generated by Django 4.1.7 on 2023-03-15 12:38
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("shortener", "0001_initial"),
(
"test_platform",
"0005_alter_basequestion_options_basequestion_order_and_more",
),
]
operations = [
migrations.AddField(
model_name="form",
name="short_link",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="shortener.link",
),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 4.1.7 on 2023-03-15 12:39
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("shortener", "0001_initial"),
("test_platform", "0006_form_short_link"),
]
operations = [
migrations.AlterField(
model_name="form",
name="short_link",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="shortener.link",
),
),
]

View File

@ -7,12 +7,13 @@
from django.utils.translation import gettext_lazy as _
from polymorphic.models import PolymorphicModel
from akarpov.common.models import BaseImageModel
from akarpov.tools.shortener.models import ShortLink
from akarpov.users.models import User
from akarpov.utils.base import SubclassesMixin
from akarpov.utils.files import user_file_upload_mixin
class Form(models.Model):
class Form(BaseImageModel, ShortLink):
name = models.CharField(max_length=200, blank=False)
description = models.TextField(blank=True)
@ -25,9 +26,6 @@ class Form(models.Model):
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
image = models.ImageField(upload_to=user_file_upload_mixin, blank=True)
image_cropped = models.ImageField(upload_to="cropped/", blank=True)
passed = models.IntegerField(default=0)
time_since = models.DateTimeField(null=True, blank=True)
time_till = models.DateTimeField(null=True, blank=True)

View File

@ -0,0 +1,5 @@
{% extends 'base.html' %}
{% block content %}
<h1>This link has been revoked</h1>
{% endblock %}

View File

@ -1,3 +1,5 @@
from abc import abstractmethod
from django.db import models
from django.urls import reverse
from model_utils.models import TimeStampedModel
@ -31,3 +33,37 @@ class LinkViewMeta(models.Model):
def __str__(self):
return f"view on {self.link.source}"
def create_model_link(sender, instance, created, **kwargs):
# had to move to models due to circular import
if created:
link = Link.objects.create(
source=instance.get_absolute_url(), creator=instance.creator
)
instance.short_link = link
instance.save()
class ShortLink(models.Model):
short_link: Link | None = models.ForeignKey(
"shortener.Link", blank=True, null=True, on_delete=models.SET_NULL
)
@classmethod
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
models.signals.post_save.connect(create_model_link, sender=cls)
@abstractmethod
def get_absolute_url(self):
...
@property
def get_short_link(self) -> str:
if self.short_link:
return reverse("short_url", kwargs={"slug": self.short_link.slug})
return reverse("tools:shortener:revoked")
class Meta:
abstract = True

View File

@ -1,18 +1,11 @@
from django.conf import settings
from akarpov.tools.shortener.models import Link
from akarpov.utils.generators import generate_charset, get_pk_from_uuid, get_str_uuid
from akarpov.tools.shortener.signals import Link
from akarpov.utils.generators import get_pk_from_uuid
length = settings.SHORTENER_SLUG_LENGTH
def generate_slug(pk: int) -> str:
if settings.SHORTENER_ADD_SLUG:
slug = generate_charset(length)
return slug + get_str_uuid(pk)
return get_str_uuid(pk)
def get_link_from_slug(slug: str, check_whole=False) -> Link | bool:
if settings.SHORTENER_ADD_SLUG and not check_whole:
payload = slug[length:]

View File

@ -1,12 +1,22 @@
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from akarpov.tools.shortener.models import Link
from akarpov.tools.shortener.services import generate_slug
from akarpov.utils.generators import generate_charset, get_str_uuid
length = settings.SHORTENER_SLUG_LENGTH
def generate_slug(pk: int) -> str:
if settings.SHORTENER_ADD_SLUG:
slug = generate_charset(length)
return slug + get_str_uuid(pk)
return get_str_uuid(pk)
@receiver(post_save, sender=Link)
def link_on_create(sender, instance: Link, created, **kwargs):
def link_on_create(sender, instance, created, **kwargs):
if created:
instance.slug = generate_slug(instance.id)
instance.save(update_fields=["slug"])

View File

@ -6,4 +6,5 @@
urlpatterns = [
path("", short_link_create_view, name="create"),
path("revoked", short_link_create_view, name="revoked"),
]

View File

@ -1,5 +1,6 @@
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.views.generic import CreateView, DetailView
from django.http import HttpResponseRedirect
from django.views.generic import CreateView, DetailView, TemplateView
from akarpov.tools.shortener.forms import LinkForm
from akarpov.tools.shortener.models import Link
@ -35,3 +36,16 @@ def get_object(self, *args, **kwargs):
link_detail_view = LinkDetailView.as_view()
class LinkRevokedView(TemplateView):
template_name = "shortener/revoked.html"
link_revoked_view = LinkRevokedView.as_view()
def redirect_view(request, slug):
# TODO: move to faster framework, like FastApi
link = get_link_from_slug(slug)
return HttpResponseRedirect(link.source)

View File

@ -0,0 +1,24 @@
# Generated by Django 4.1.7 on 2023-03-15 12:38
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("shortener", "0001_initial"),
("users", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="user",
name="short_link",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="shortener.link",
),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 4.1.7 on 2023-03-15 12:39
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("shortener", "0001_initial"),
("users", "0002_user_short_link"),
]
operations = [
migrations.AlterField(
model_name="user",
name="short_link",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="shortener.link",
),
),
]

View File

@ -1,12 +1,13 @@
from django.contrib.auth.models import AbstractUser
from django.db.models import CharField, ImageField, TextField
from django.db.models import CharField, TextField
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from akarpov.utils.files import user_file_upload_mixin
from akarpov.common.models import BaseImageModel
from akarpov.tools.shortener.models import ShortLink
class User(AbstractUser):
class User(AbstractUser, BaseImageModel, ShortLink):
"""
Default custom user model for akarpov.
If adding fields that need to be filled at user signup,
@ -16,8 +17,6 @@ class User(AbstractUser):
#: First and last name do not cover name patterns around the globe
name = CharField(_("Name of User"), blank=True, max_length=255)
about = TextField(_("Description"), blank=True, max_length=100)
image = ImageField(upload_to=user_file_upload_mixin, blank=True)
image_cropped = ImageField(upload_to="cropped/", blank=True)
first_name = None # type: ignore
last_name = None # type: ignore

View File

@ -10,7 +10,7 @@
SpectacularSwaggerView,
)
from akarpov.tools.shortener.views import link_detail_view
from akarpov.tools.shortener.views import redirect_view
urlpatterns = [
path("home", TemplateView.as_view(template_name="pages/home.html"), name="home"),
@ -23,12 +23,13 @@
path(settings.ADMIN_URL, admin.site.urls),
# User management
path("users/", include("akarpov.users.urls", namespace="users")),
path("files/", include("akarpov.files.urls", namespace="files")),
path("forms/", include("akarpov.test_platform.urls", namespace="forms")),
path("tools/", include("akarpov.tools.urls", namespace="tools")),
path("ckeditor/", include("ckeditor_uploader.urls")),
path("accounts/", include("allauth.urls")),
path("", include("akarpov.blog.urls", namespace="blog")),
path("s/<str:slug>", link_detail_view, name="short_url"),
path("s/<str:slug>", redirect_view, name="short_url"),
# Your stuff: custom urls includes go here
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)