Implement image gallery for establishments

This commit is contained in:
Kumi 2021-05-30 12:30:14 +02:00
parent 4ccd5784c4
commit 553b6fae23
10 changed files with 221 additions and 8 deletions

View file

@ -1,3 +1,5 @@
from django.contrib import admin
from urlaubsauktion.admin import joker_admin as admin
# Register your models here.
from .models import Image
admin.register(Image)

4
gallery/helpers.py Normal file
View file

@ -0,0 +1,4 @@
from django.conf import settings
def get_upload_path(instance, filename):
return instance.upload_path or settings.GALLERY_UPLOAD_PATH

13
gallery/mixins.py Normal file
View file

@ -0,0 +1,13 @@
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
from .models import Image
class GalleryMixin(models.Model):
image_set = GenericRelation(Image)
def add_image(self, image, upload_path=None):
Image.objects.create(image=image, content_object=self, upload_path=upload_path)
class Meta:
abstract = True

View file

@ -1,3 +1,20 @@
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
# Create your models here.
from .helpers import get_upload_path
class Image(models.Model):
image = models.ImageField(upload_to=get_upload_path)
title = models.CharField(max_length=128, null=True, blank=True)
comment = models.TextField(null=True, blank=True)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
upload_path = models.CharField(max_length=256, null=True, blank=True)
@property
def url(self):
return self.image.url

View file

@ -3,6 +3,7 @@ from django.contrib.gis.db import models
from .features import *
from localauth.models import User, Profile, LocationMixin, ImageMixin, PhoneMixin
from gallery.mixins import GalleryMixin
from django_countries.fields import CountryField
@ -11,7 +12,7 @@ class PartnerProfile(Profile):
def roomcategory_set(self):
return RoomCategory.objects.filter(establishment__in=self.establishment_set.all())
class Establishment(LocationMixin, ImageMixin, PhoneMixin):
class Establishment(LocationMixin, ImageMixin, PhoneMixin, GalleryMixin):
owner = models.ForeignKey(PartnerProfile, models.CASCADE)
name = models.CharField("Name", max_length=64)
stars = models.IntegerField("Sterne", null=True, blank=True)
@ -40,7 +41,7 @@ class Establishment(LocationMixin, ImageMixin, PhoneMixin):
return querysets[0].union(*querysets[1:])
class RoomCategory(ImageMixin):
class RoomCategory(GalleryMixin):
establishment = models.ForeignKey(Establishment, models.CASCADE)
name = models.CharField("Name", max_length=64)
average_price = models.DecimalField("Durchschnittspreis / Nacht", max_digits=10, decimal_places=2)

View file

@ -1,7 +1,7 @@
from django.urls import path, reverse_lazy
from django.views.generic import RedirectView
from .views import PartnerRegistrationView, PartnerProfileView, OffersListView, EstablishmentsListView, EstablishmentRequestView, PartnerDashboardView, EstablishmentVerificationView, RoomCategoryListView
from .views import PartnerRegistrationView, PartnerProfileView, OffersListView, EstablishmentsListView, EstablishmentRequestView, PartnerDashboardView, EstablishmentVerificationView, RoomCategoryListView, EstablishmentGalleryManagementView
app_name = "partners"
@ -10,6 +10,7 @@ urlpatterns = [
path('profile/', PartnerProfileView.as_view(), name="profile"),
path('establishments/', EstablishmentsListView.as_view(), name="establishments"),
path('establishments/<int:id>/', RoomCategoryListView.as_view(), name="roomcategories"),
path('establishments/<int:id>/gallery/', EstablishmentGalleryManagementView.as_view(), name="establishment_gallery"),
path('establishments/validate/', EstablishmentVerificationView.as_view(), name="establishment_verify"),
path('establishments/register/', EstablishmentRequestView.as_view(), name="establishment_register"),
path('offers/', OffersListView.as_view(), name="offers"),

View file

@ -11,6 +11,7 @@ from .forms import VerificationForm
from auction.models import Inquiry, Offer
from public.mixins import InConstructionMixin
from localauth.mixins import LoginRequiredMixin, SuperUserRequiredMixin
from gallery.models import Image
class PartnerRegistrationView(InConstructionMixin, LoginRequiredMixin, CreateView):
model = PartnerProfile
@ -153,3 +154,48 @@ class EstablishmentVerificationView(SuperUserRequiredMixin, FormView):
messages.success(self.request, "Unterkunft %s bestätigt!" % eobj[0].name)
return HttpResponseRedirect(reverse_lazy("partners:establishment_verify"))
class EstablishmentGalleryManagementView(PartnerProfileRequiredMixin, CreateView, ListView):
model = Image
template_name = "partners/establishment_gallery_manage.html"
fields = ["image", "title", "comment"]
def dispatch(self, request, *args, **kwargs):
self.establishment = self.get_establishment()
self.object_list = self.get_queryset()
if not self.establishment:
return redirect("partners:establishment_register")
return super().dispatch(request, *args, **kwargs)
def get_establishment(self):
establishment = self.kwargs.get("id", None)
kwargs = {"owner": self.request.user.partnerprofile}
if establishment:
kwargs["id"] = establishment
return get_object_or_404(Establishment, **kwargs)
else:
return Establishment.objects.filter(**kwargs).first()
def get_queryset(self):
establishment = self.establishment
return establishment.image_set.all()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["establishment"] = self.establishment
return context
def form_valid(self, form):
form.instance.content_object = self.establishment
for filename, file in self.request.FILES.items():
name = self.request.FILES[filename].name
form.instance.upload_path = f"userfiles/{self.request.user.id}/{name}"
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("partners:establishment_gallery", args=[self.establishment.id])

View file

@ -0,0 +1,7 @@
from django import template
register = template.Library()
@register.simple_tag
def splitter(l, c=3):
return [l[i:i+c] for i in range(0, len(l), c)]

View file

@ -0,0 +1,120 @@
{% extends "partners/base.html" %}
{% load i18n %}
{% load bootstrap4 %}
{% load splitter %}
{% block "dashboardcontent" %}
<div class="col-12 col-md-10 col-lg-10 dashboard-content booking-trips">
<h2 class="dash-content-title">Deine Bilder</h2>
<div class="dash-content-title"><small>für {{ establishment.name }}</small></div>
<button
class="position-relative btn-primary text-center"
type="button"
data-target="#create-image" data-toggle="modal"
>Bild hochladen</button>
<!-- Carousel wrapper -->
<div
id="carouselMultiItemExample"
class="carousel slide carousel-dark text-center"
data-ride="carousel"
>
<!-- Controls -->
<div class="d-flex justify-content-center mb-4">
<button
class="carousel-control-prev position-relative btn-secondary"
type="button"
data-target="#carouselMultiItemExample"
data-slide="prev"
>
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Zurück</span>
</button>
<button
class="carousel-control-next position-relative btn-secondary"
type="button"
data-target="#carouselMultiItemExample"
data-slide="next"
>
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Weiter</span>
</button>
</div>
<!-- Inner -->
<div class="carousel-inner py-4">
{% splitter object_list 3 as chunks %}
{% for chunk in chunks %}
<!-- Single item -->
<div class="carousel-item {% if forloop.first %}active{% endif %}">
<div class="container">
<div class="row">
{% for image in chunk %}
<div class="col-lg-4 {% if forloop.first %}d-none d-lg-block{% endif %}">
<div class="card">
<img src="{{ image.url }}" class="card-img-top" alt="{{ image.title }}" />
<div class="card-body">
<h5 class="card-title">{{ image.title }}</h5>
<p class="card-text">{{ image.comment }}</p>
<a href="#!" class="btn btn-primary">Löschen</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
</div>
<!-- Inner -->
</div>
<!-- Carousel wrapper -->
</div>
{% endblock %}
{% block "modal" %}
<div id="create-image" class="modal custom-modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Bild hochladen</h3>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div><!-- end modal-header -->
<div class="modal-body">
<form enctype="multipart/form-data" method="POST">
{% csrf_token %}
{% bootstrap_form form %}
<button class="btn btn-orange btn-block">Hochladen...</button>
</form>
</div><!-- end modal-bpdy -->
</div><!-- end modal-content -->
</div><!-- end modal-dialog -->
</div><!-- end edit-profile -->
<!-- Modal 1 -->
<div
class="modal fade"
id="exampleModal1"
tabindex="-1"
aria-labelledby="exampleModal1Label"
aria-hidden="true"
>
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="ratio ratio-16x9">
<iframe
src="https://www.youtube.com/embed/A3PDXmYoF5U"
title="YouTube video"
allowfullscreen width="100%" height="100%"
></iframe>
</div>
<div class="text-center py-3">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Schließen
</button>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -134,3 +134,5 @@ STATICFILES_STORAGE = 'storages.backends.s3boto3.S3StaticStorage' if ENABLE_S3_S
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
PHONENUMBER_DEFAULT_REGION = JOKER_COUNTRIES[0]
GALLERY_UPLOAD_PATH = "/userfiles/"