mirror of
https://github.com/Alexander-D-Karpov/akarpov
synced 2024-11-22 02:46:33 +03:00
added more common models, added short link base model, updated shortener
This commit is contained in:
parent
39ef0fb06b
commit
9b6934288c
24
akarpov/blog/migrations/0003_post_short_link.py
Normal file
24
akarpov/blog/migrations/0003_post_short_link.py
Normal 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",
|
||||
),
|
||||
),
|
||||
]
|
25
akarpov/blog/migrations/0004_alter_post_short_link.py
Normal file
25
akarpov/blog/migrations/0004_alter_post_short_link.py
Normal 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",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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):
|
||||
|
|
|
@ -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
|
|
@ -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",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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
9
akarpov/files/urls.py
Normal 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"),
|
||||
]
|
|
@ -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()
|
27
akarpov/test_platform/migrations/0006_form_short_link.py
Normal file
27
akarpov/test_platform/migrations/0006_form_short_link.py
Normal 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",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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)
|
||||
|
|
0
akarpov/test_platform/templates/files/folder.html
Normal file
0
akarpov/test_platform/templates/files/folder.html
Normal file
0
akarpov/test_platform/templates/files/view.html
Normal file
0
akarpov/test_platform/templates/files/view.html
Normal file
5
akarpov/test_platform/templates/shortener/revoked.html
Normal file
5
akarpov/test_platform/templates/shortener/revoked.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>This link has been revoked</h1>
|
||||
{% endblock %}
|
|
@ -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
|
||||
|
|
|
@ -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:]
|
||||
|
|
|
@ -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"])
|
||||
|
|
|
@ -6,4 +6,5 @@
|
|||
|
||||
urlpatterns = [
|
||||
path("", short_link_create_view, name="create"),
|
||||
path("revoked", short_link_create_view, name="revoked"),
|
||||
]
|
||||
|
|
|
@ -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)
|
||||
|
|
24
akarpov/users/migrations/0002_user_short_link.py
Normal file
24
akarpov/users/migrations/0002_user_short_link.py
Normal 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",
|
||||
),
|
||||
),
|
||||
]
|
25
akarpov/users/migrations/0003_alter_user_short_link.py
Normal file
25
akarpov/users/migrations/0003_alter_user_short_link.py
Normal 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",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user