mirror of
				https://github.com/Alexander-D-Karpov/akarpov
				synced 2025-10-31 23:47:30 +03:00 
			
		
		
		
	created image gallery
This commit is contained in:
		
							parent
							
								
									716f798ce1
								
							
						
					
					
						commit
						0d45e5bac2
					
				|  | @ -1,6 +1,17 @@ | ||||||
| from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin | from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin | ||||||
|  | from django.views.generic.detail import SingleObjectMixin | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class SuperUserRequiredMixin(LoginRequiredMixin, UserPassesTestMixin): | class SuperUserRequiredMixin(LoginRequiredMixin, UserPassesTestMixin): | ||||||
|     def test_func(self): |     def test_func(self): | ||||||
|         return self.request.user.is_superuser |         return self.request.user.is_superuser | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class HasPermissions(SingleObjectMixin): | ||||||
|  |     def get_context_data(self, **kwargs): | ||||||
|  |         context = super().get_context_data(**kwargs) | ||||||
|  |         has_perm = False | ||||||
|  |         if self.request.user.is_authentificated: | ||||||
|  |             has_perm = self.object.user == self.request.user | ||||||
|  |         context["has_permissions"] = has_perm | ||||||
|  |         return context | ||||||
|  |  | ||||||
							
								
								
									
										0
									
								
								akarpov/gallery/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								akarpov/gallery/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										6
									
								
								akarpov/gallery/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								akarpov/gallery/admin.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | ||||||
|  | from django.contrib import admin | ||||||
|  | 
 | ||||||
|  | from akarpov.gallery.models import Collection, Image | ||||||
|  | 
 | ||||||
|  | admin.site.register(Collection) | ||||||
|  | admin.site.register(Image) | ||||||
							
								
								
									
										12
									
								
								akarpov/gallery/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								akarpov/gallery/apps.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | ||||||
|  | from django.apps import AppConfig | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class GalleryConfig(AppConfig): | ||||||
|  |     name = "akarpov.gallery" | ||||||
|  |     verbose_name = "Gallery" | ||||||
|  | 
 | ||||||
|  |     def ready(self): | ||||||
|  |         try: | ||||||
|  |             import akarpov.gallery.signals  # noqa F401 | ||||||
|  |         except ImportError: | ||||||
|  |             pass | ||||||
							
								
								
									
										134
									
								
								akarpov/gallery/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								akarpov/gallery/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,134 @@ | ||||||
|  | # Generated by Django 4.2 on 2023-04-29 08:45 | ||||||
|  | 
 | ||||||
|  | import akarpov.utils.files | ||||||
|  | from django.conf import settings | ||||||
|  | from django.db import migrations, models | ||||||
|  | import django.db.models.deletion | ||||||
|  | import django_extensions.db.fields | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     initial = True | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||||||
|  |         ("shortener", "0001_initial"), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name="Collection", | ||||||
|  |             fields=[ | ||||||
|  |                 ( | ||||||
|  |                     "id", | ||||||
|  |                     models.BigAutoField( | ||||||
|  |                         auto_created=True, | ||||||
|  |                         primary_key=True, | ||||||
|  |                         serialize=False, | ||||||
|  |                         verbose_name="ID", | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ("slug", models.SlugField(blank=True, max_length=20, unique=True)), | ||||||
|  |                 ( | ||||||
|  |                     "created", | ||||||
|  |                     django_extensions.db.fields.CreationDateTimeField( | ||||||
|  |                         auto_now_add=True, verbose_name="created" | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ( | ||||||
|  |                     "modified", | ||||||
|  |                     django_extensions.db.fields.ModificationDateTimeField( | ||||||
|  |                         auto_now=True, verbose_name="modified" | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ("name", models.CharField(blank=True, max_length=250)), | ||||||
|  |                 ("description", models.TextField()), | ||||||
|  |                 ("public", models.BooleanField(default=False)), | ||||||
|  |                 ( | ||||||
|  |                     "short_link", | ||||||
|  |                     models.ForeignKey( | ||||||
|  |                         blank=True, | ||||||
|  |                         null=True, | ||||||
|  |                         on_delete=django.db.models.deletion.SET_NULL, | ||||||
|  |                         to="shortener.link", | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ( | ||||||
|  |                     "user", | ||||||
|  |                     models.ForeignKey( | ||||||
|  |                         on_delete=django.db.models.deletion.CASCADE, | ||||||
|  |                         related_name="collections", | ||||||
|  |                         to=settings.AUTH_USER_MODEL, | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 "get_latest_by": "modified", | ||||||
|  |                 "abstract": False, | ||||||
|  |             }, | ||||||
|  |         ), | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name="Image", | ||||||
|  |             fields=[ | ||||||
|  |                 ( | ||||||
|  |                     "id", | ||||||
|  |                     models.BigAutoField( | ||||||
|  |                         auto_created=True, | ||||||
|  |                         primary_key=True, | ||||||
|  |                         serialize=False, | ||||||
|  |                         verbose_name="ID", | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ("image_cropped", models.ImageField(blank=True, upload_to="cropped/")), | ||||||
|  |                 ("slug", models.SlugField(blank=True, max_length=20, unique=True)), | ||||||
|  |                 ( | ||||||
|  |                     "created", | ||||||
|  |                     django_extensions.db.fields.CreationDateTimeField( | ||||||
|  |                         auto_now_add=True, verbose_name="created" | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ( | ||||||
|  |                     "modified", | ||||||
|  |                     django_extensions.db.fields.ModificationDateTimeField( | ||||||
|  |                         auto_now=True, verbose_name="modified" | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ( | ||||||
|  |                     "image", | ||||||
|  |                     models.ImageField( | ||||||
|  |                         upload_to=akarpov.utils.files.user_file_upload_mixin | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ( | ||||||
|  |                     "collection", | ||||||
|  |                     models.ForeignKey( | ||||||
|  |                         on_delete=django.db.models.deletion.CASCADE, | ||||||
|  |                         related_name="images", | ||||||
|  |                         to="gallery.collection", | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ( | ||||||
|  |                     "short_link", | ||||||
|  |                     models.ForeignKey( | ||||||
|  |                         blank=True, | ||||||
|  |                         null=True, | ||||||
|  |                         on_delete=django.db.models.deletion.SET_NULL, | ||||||
|  |                         to="shortener.link", | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |                 ( | ||||||
|  |                     "user", | ||||||
|  |                     models.ForeignKey( | ||||||
|  |                         on_delete=django.db.models.deletion.CASCADE, | ||||||
|  |                         related_name="images", | ||||||
|  |                         to=settings.AUTH_USER_MODEL, | ||||||
|  |                     ), | ||||||
|  |                 ), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |                 "get_latest_by": "modified", | ||||||
|  |                 "abstract": False, | ||||||
|  |             }, | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
							
								
								
									
										0
									
								
								akarpov/gallery/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								akarpov/gallery/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										38
									
								
								akarpov/gallery/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								akarpov/gallery/models.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | ||||||
|  | from django.db import models | ||||||
|  | from django.urls import reverse | ||||||
|  | from django_extensions.db.models import TimeStampedModel | ||||||
|  | 
 | ||||||
|  | from akarpov.common.models import BaseImageModel | ||||||
|  | from akarpov.tools.shortener.models import ShortLink | ||||||
|  | from akarpov.utils.files import user_file_upload_mixin | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Collection(TimeStampedModel, ShortLink): | ||||||
|  |     name = models.CharField(max_length=250, blank=True) | ||||||
|  |     description = models.TextField() | ||||||
|  |     public = models.BooleanField(default=False) | ||||||
|  |     user = models.ForeignKey( | ||||||
|  |         "users.User", related_name="collections", on_delete=models.CASCADE | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     def get_absolute_url(self): | ||||||
|  |         return reverse("gallery:collection", kwargs={"slug": self.slug}) | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return self.name | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Image(TimeStampedModel, ShortLink, BaseImageModel): | ||||||
|  |     collection = models.ForeignKey( | ||||||
|  |         "Collection", related_name="images", on_delete=models.CASCADE | ||||||
|  |     ) | ||||||
|  |     user = models.ForeignKey( | ||||||
|  |         "users.User", related_name="images", on_delete=models.CASCADE | ||||||
|  |     ) | ||||||
|  |     image = models.ImageField(upload_to=user_file_upload_mixin, blank=False, null=False) | ||||||
|  | 
 | ||||||
|  |     def get_absolute_url(self): | ||||||
|  |         return reverse("gallery:view", kwargs={"slug": self.slug}) | ||||||
|  | 
 | ||||||
|  |     def __str__(self): | ||||||
|  |         return self.image.name | ||||||
							
								
								
									
										10
									
								
								akarpov/gallery/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								akarpov/gallery/urls.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | from django.urls import path | ||||||
|  | 
 | ||||||
|  | from akarpov.gallery.views import collection_view, image_view, list_collections_view | ||||||
|  | 
 | ||||||
|  | app_name = "gallery" | ||||||
|  | urlpatterns = [ | ||||||
|  |     path("", list_collections_view, name="list"), | ||||||
|  |     path("<str:slug>", collection_view, name="collection"), | ||||||
|  |     path("image/<str:slug>", image_view, name="view"), | ||||||
|  | ] | ||||||
							
								
								
									
										33
									
								
								akarpov/gallery/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								akarpov/gallery/views.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | ||||||
|  | from django.views import generic | ||||||
|  | 
 | ||||||
|  | from akarpov.common.views import HasPermissions | ||||||
|  | from akarpov.gallery.models import Collection | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ListCollectionsView(generic.ListView): | ||||||
|  |     model = Collection | ||||||
|  |     template_name = "gallery/list.html" | ||||||
|  | 
 | ||||||
|  |     def get_queryset(self): | ||||||
|  |         if self.request.user.is_authenticated: | ||||||
|  |             return self.request.user.collections.all() | ||||||
|  |         return Collection.objects.filter(public=True) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | list_collections_view = ListCollectionsView.as_view() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class CollectionView(generic.DetailView, HasPermissions): | ||||||
|  |     model = Collection | ||||||
|  |     template_name = "gallery/collection.html" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | collection_view = CollectionView.as_view() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ImageView(generic.DetailView, HasPermissions): | ||||||
|  |     model = Collection | ||||||
|  |     template_name = "gallery/image.html" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | image_view = ImageView.as_view() | ||||||
							
								
								
									
										1
									
								
								akarpov/templates/gallery/collection.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								akarpov/templates/gallery/collection.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | {% extends 'base.html' %} | ||||||
							
								
								
									
										1
									
								
								akarpov/templates/gallery/image.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								akarpov/templates/gallery/image.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | {% extends 'base.html' %} | ||||||
							
								
								
									
										1
									
								
								akarpov/templates/gallery/list.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								akarpov/templates/gallery/list.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | {% extends 'base.html' %} | ||||||
|  | @ -1,29 +0,0 @@ | ||||||
| {% load markdown_extras %} |  | ||||||
| 
 |  | ||||||
| {% block content %} |  | ||||||
|     <br> |  | ||||||
|     <a class="mx-5" style="text-decoration: none; color: black" href="{% url "about" %}#projects"><i class="bi-arrow-left"></i> back to all projects</a> |  | ||||||
|     <div class="row m-5 mb-5 g-1 row-cols-1 row-cols-md-2"> |  | ||||||
|         <div class="col"> |  | ||||||
|             <img class="img-fluid" src="{{ project.image.url }}" alt=""/> |  | ||||||
|         </div> |  | ||||||
|         <div class="col text-center d-flex row align-items-center"> |  | ||||||
|             <div class="align-items-center m-3"> |  | ||||||
|                 <div class="align-self-center"> |  | ||||||
|                     <h1 class="fs-2">{{ project.name }}</h1> |  | ||||||
|                     <p class="lead">{{ project.md | markdown | safe }}</p> |  | ||||||
|                     <p>{{ project.description }}</p> |  | ||||||
|                     <p>Created: {{ project.created|date:"d M Y" }}</p> |  | ||||||
|                     <p> |  | ||||||
|                         {% if project.source_link %} |  | ||||||
|                         <a style="text-decoration: none; color: black" class="m-2" href="{{ project.source_link }}"><i class="bi bi-file-earmark-code"></i> Source code</a> |  | ||||||
|                         {% endif %} |  | ||||||
|                         {% if project.view_link %} |  | ||||||
|                         <a style="text-decoration: none; color: black" class="m-2" href="{{ project.view_link }}"><i class="bi bi-box-arrow-up-right"></i> View project</a> |  | ||||||
|                         {% endif %} |  | ||||||
|                     </p> |  | ||||||
|                 </div> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|     </div> |  | ||||||
| {% endblock %} |  | ||||||
|  | @ -11,7 +11,7 @@ class ShortLinkCreateView(CreateView): | ||||||
|     model = Link |     model = Link | ||||||
|     form_class = LinkForm |     form_class = LinkForm | ||||||
| 
 | 
 | ||||||
|     template_name = "shortener/create.html" |     template_name = "tools/shortener/create.html" | ||||||
| 
 | 
 | ||||||
|     def form_valid(self, form): |     def form_valid(self, form): | ||||||
|         if self.request.user.is_authenticated: |         if self.request.user.is_authenticated: | ||||||
|  | @ -24,7 +24,7 @@ def form_valid(self, form): | ||||||
| 
 | 
 | ||||||
| class LinkDetailView(DetailView): | class LinkDetailView(DetailView): | ||||||
| 
 | 
 | ||||||
|     template_name = "shortener/view.html" |     template_name = "tools/shortener/view.html" | ||||||
| 
 | 
 | ||||||
|     def get_object(self, *args, **kwargs): |     def get_object(self, *args, **kwargs): | ||||||
|         link = get_link_from_slug(self.kwargs["slug"]) |         link = get_link_from_slug(self.kwargs["slug"]) | ||||||
|  | @ -39,7 +39,7 @@ def get_object(self, *args, **kwargs): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class LinkRevokedView(TemplateView): | class LinkRevokedView(TemplateView): | ||||||
|     template_name = "shortener/revoked.html" |     template_name = "tools/shortener/revoked.html" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| link_revoked_view = LinkRevokedView.as_view() | link_revoked_view = LinkRevokedView.as_view() | ||||||
|  |  | ||||||
|  | @ -136,7 +136,7 @@ | ||||||
|     "health_check.contrib.redis", |     "health_check.contrib.redis", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| ALL_AUTH_PROVIDERS = [ | ALLAUTH_PROVIDERS = [ | ||||||
|     "allauth.socialaccount.providers.github", |     "allauth.socialaccount.providers.github", | ||||||
|     # "allauth.socialaccount.providers.google", |     # "allauth.socialaccount.providers.google", | ||||||
|     # "allauth.socialaccount.providers.telegram", |     # "allauth.socialaccount.providers.telegram", | ||||||
|  | @ -149,6 +149,7 @@ | ||||||
|     "akarpov.blog", |     "akarpov.blog", | ||||||
|     "akarpov.files", |     "akarpov.files", | ||||||
|     "akarpov.music", |     "akarpov.music", | ||||||
|  |     "akarpov.gallery", | ||||||
|     "akarpov.pipeliner", |     "akarpov.pipeliner", | ||||||
|     "akarpov.test_platform", |     "akarpov.test_platform", | ||||||
|     "akarpov.tools.shortener", |     "akarpov.tools.shortener", | ||||||
|  | @ -156,7 +157,7 @@ | ||||||
| ] | ] | ||||||
| # https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps | # https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps | ||||||
| INSTALLED_APPS = ( | INSTALLED_APPS = ( | ||||||
|     DJANGO_APPS + LOCAL_APPS + THIRD_PARTY_APPS + HEALTH_CHECKS + ALL_AUTH_PROVIDERS |     DJANGO_APPS + LOCAL_APPS + THIRD_PARTY_APPS + HEALTH_CHECKS + ALLAUTH_PROVIDERS | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| # MIGRATIONS | # MIGRATIONS | ||||||
|  |  | ||||||
|  | @ -36,6 +36,7 @@ | ||||||
|     path("music/", include("akarpov.music.urls", namespace="music")), |     path("music/", include("akarpov.music.urls", namespace="music")), | ||||||
|     path("forms/", include("akarpov.test_platform.urls", namespace="forms")), |     path("forms/", include("akarpov.test_platform.urls", namespace="forms")), | ||||||
|     path("tools/", include("akarpov.tools.urls", namespace="tools")), |     path("tools/", include("akarpov.tools.urls", namespace="tools")), | ||||||
|  |     path("gallery/", include("akarpov.gallery.urls", namespace="gallery")), | ||||||
|     path("ckeditor/", include("ckeditor_uploader.urls")), |     path("ckeditor/", include("ckeditor_uploader.urls")), | ||||||
|     path("accounts/", include("allauth.urls")), |     path("accounts/", include("allauth.urls")), | ||||||
|     path("", include("akarpov.blog.urls", namespace="blog")), |     path("", include("akarpov.blog.urls", namespace="blog")), | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user