mirror of
				https://github.com/Alexander-D-Karpov/akarpov
				synced 2025-10-31 09:17:24 +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.views.generic.detail import SingleObjectMixin | ||||
| 
 | ||||
| 
 | ||||
| class SuperUserRequiredMixin(LoginRequiredMixin, UserPassesTestMixin): | ||||
|     def test_func(self): | ||||
|         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 | ||||
|     form_class = LinkForm | ||||
| 
 | ||||
|     template_name = "shortener/create.html" | ||||
|     template_name = "tools/shortener/create.html" | ||||
| 
 | ||||
|     def form_valid(self, form): | ||||
|         if self.request.user.is_authenticated: | ||||
|  | @ -24,7 +24,7 @@ def form_valid(self, form): | |||
| 
 | ||||
| class LinkDetailView(DetailView): | ||||
| 
 | ||||
|     template_name = "shortener/view.html" | ||||
|     template_name = "tools/shortener/view.html" | ||||
| 
 | ||||
|     def get_object(self, *args, **kwargs): | ||||
|         link = get_link_from_slug(self.kwargs["slug"]) | ||||
|  | @ -39,7 +39,7 @@ def get_object(self, *args, **kwargs): | |||
| 
 | ||||
| 
 | ||||
| class LinkRevokedView(TemplateView): | ||||
|     template_name = "shortener/revoked.html" | ||||
|     template_name = "tools/shortener/revoked.html" | ||||
| 
 | ||||
| 
 | ||||
| link_revoked_view = LinkRevokedView.as_view() | ||||
|  |  | |||
|  | @ -136,7 +136,7 @@ | |||
|     "health_check.contrib.redis", | ||||
| ] | ||||
| 
 | ||||
| ALL_AUTH_PROVIDERS = [ | ||||
| ALLAUTH_PROVIDERS = [ | ||||
|     "allauth.socialaccount.providers.github", | ||||
|     # "allauth.socialaccount.providers.google", | ||||
|     # "allauth.socialaccount.providers.telegram", | ||||
|  | @ -149,6 +149,7 @@ | |||
|     "akarpov.blog", | ||||
|     "akarpov.files", | ||||
|     "akarpov.music", | ||||
|     "akarpov.gallery", | ||||
|     "akarpov.pipeliner", | ||||
|     "akarpov.test_platform", | ||||
|     "akarpov.tools.shortener", | ||||
|  | @ -156,7 +157,7 @@ | |||
| ] | ||||
| # https://docs.djangoproject.com/en/dev/ref/settings/#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 | ||||
|  |  | |||
|  | @ -36,6 +36,7 @@ | |||
|     path("music/", include("akarpov.music.urls", namespace="music")), | ||||
|     path("forms/", include("akarpov.test_platform.urls", namespace="forms")), | ||||
|     path("tools/", include("akarpov.tools.urls", namespace="tools")), | ||||
|     path("gallery/", include("akarpov.gallery.urls", namespace="gallery")), | ||||
|     path("ckeditor/", include("ckeditor_uploader.urls")), | ||||
|     path("accounts/", include("allauth.urls")), | ||||
|     path("", include("akarpov.blog.urls", namespace="blog")), | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user