Hi Ken,
thank you very much for your kind answer.
Here are my files:
settings.py
"""
Django settings for mantra_kirtan project.
Generated by 'django-admin startproject' using Django 4.1.7.
For more information on this file, see
https://docs.djangoproject.com/en/4.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.1/ref/settings/
"""
from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-n5d&_s*s96m6s=w+lb3$mkwb67qibel)f)gj4iw^f_*@i_!2mo'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
ALLOWED_HOSTS = ['kirtanloveberlin']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'kirtanloveberlin',
'schedule',
#'recurrence',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'mantra_kirtan.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'mantra_kirtan.wsgi.application'
# Database
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'mantra',
'USER': 'mantra',
'PASSWORD': 'mantra',
'HOST': '127.0.0.1',
'PORT': '5432',
},
}
# Password validation
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Europe/Berlin'
USE_I18N = True
USE_TZ = False
# Static files (CSS, JavaScript, Images)
STATIC_URL = '/static/'
#STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
# Media files (Uploaded Images, Documents, etc.)
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Default primary key field type
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# ...
DEBUG = False
ALLOWED_HOSTS = ['mantra-kirtan.de', 'kirtanloveberlin.com']
admin.py
from django.contrib import admin
from .models import CustomEvent
#from .models import CustomEvent, Termin
# Registriere das ursprüngliche CustomEvent Modell
admin.site.register(CustomEvent)
# # Registriere das neue MeinTermin Modell
# admin.site.register(Termin)
html template
{% load static %}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Kirtan Love Berlin</title>
<link href="https://cdn.jsdelivr.net/npm/fullcalendar@5/main.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@5/main.min.js"></script>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<style>
body {
font-family: 'Arial', sans-serif;
background-color: #fff3e0;
color: #5a2d0c;
line-height: 1.6;
}
.container {
width: 80%;
margin: auto;
overflow: hidden;
}
#calendar-container, #event-details {
background: #ffeedd;
border: 1px solid #ffb380;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
margin-top: 20px;
}
#toggle-calendar {
background: #ff9e80;
color: #fff;
padding: 10px 15px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
margin-top: 20px;
}
#toggle-calendar:hover {
background: #ff6e40;
}
#footer {
text-align: center;
padding: 20px;
background: #ffb380;
color: #5a2d0c;
margin-top: 20px;
}
#footer p {
font-size: 2em;
margin: 0;
font-family: 'Courier New', Courier, monospace;
}
.introduction-text {
margin-bottom: 20px;
background: #ffeedd;
padding: 20px;
border-radius: 8px;
}
.introduction-text h1 {
color: #ff9e80;
}
.event-image {
max-width: 100%;
height: auto;
padding: 10px 0;
}
.bold {
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<div class="introduction-text">
<h1>Experience the Power of Mantra Meditation!</h1>
<p>
<img src="{% static 'images/1.webp' %}" alt="Inspirierendes Bild für Kirtan Love Berlin, das Harmonie und innere Ruhe darstellt" width=100%>
Join us for an <span class="bold">unforgettable afternoon</span> of <span class="bold">soulful and dynamic music</span>, and a
<span class="bold">pleasant meditation.</span>
<img src="{% static 'images/2.webp' %}" alt="Inspirierendes Bild für Kirtan Love Berlin, das Harmonie und innere Ruhe darstellt" width=100%>
The Kirtan will <span class="bold">touch your heart</span> and helps you find <span class="bold">inner peace and harmony</span>.
</p>
<img src="{% static 'images/kirtanlove_1.png' %}" alt="Inspirierendes Bild für Kirtan Love Berlin, das Harmonie und innere Ruhe darstellt" width=100%>
<p>
Immerse yourself in the <span class="bold">soothing power of mantras</span>, accompanied by <span class="bold">enchanting melodies</span>
that will transport you to a state of <span class="bold">pure bliss</span>.
<img src="{% static 'images/3.webp' %}" alt="Inspirierendes Bild für Kirtan Love Berlin, das Harmonie und innere Ruhe darstellt" width=100%>
This in-person event promises to be a <span class="bold">transformative experience</span>,
leaving you <span class="bold">refreshed and inspired</span>.
</p>
<img src="{% static 'images/4.webp' %}" alt="Inspirierendes Bild für Kirtan Love Berlin, das Harmonie und innere Ruhe darstellt" width=100%>
<h2 class="bold">What can you expect:</h2>
<ul>
<li class="list-item">A profound <span class="bold">Mantra Meditation</span> and
<span class="bold">Kirtan</span> – an experience that must be lived to fully
understand its impact.</li>
<li class="list-item">Exchange in a <span class="bold">warm, welcoming atmosphere</span>.</li>
<li class="list-item">A <span class="bold">delicious vegetarian meal</span> (vegan options available – please let us know in advance).</li>
</ul>
<h2 class="bold">For Whom:</h2>
<ul>
<li class="list-item"><span class="bold">Beginners and newcomers</span> are especially welcome! Our events are designed to be accessible and enjoyable for everyone, regardless of experience level.</li>
<li class="list-item">Those seeking <span class="bold">relaxation, inner peace, and community connection</span> will find a welcoming and supportive atmosphere.</li>
<li class="list-item">Anyone interested in <span class="bold">exploring meditation, music, and mindful living</span> is invited to join us.</li>
</ul>
<h2 class="bold">What to Bring:</h2>
<ul>
<li class="list-item">No need to bring anything - <span class="bold"> just your presence!</span></li>
</ul>
<img src="{% static 'images/kirtanlove_2.webp' %}" alt="Inspirierendes Bild für Kirtan Love Berlin, das Harmonie und innere Ruhe darstellt" width=100%>
</div>
<button id="toggle-calendar">Find Your Kirtan Event In The Calendar, Click On It AND See Details Below.</button>
<div id="calendar-container">
<div id="calendar"></div>
</div>
<div id="event-details"></div>
<div id="footer">
<p>Kirtan Love Berlin</p>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
editable: false,
timeZone: 'Europe/Berlin',
events: '/kirtanloveberlin/calendar/',
eventTimeFormat: {
hour: '2-digit',
minute: '2-digit',
hour12: false
},
eventClick: function(info) {
var start = info.event.start.toISOString();
var end = info.event.end ? info.event.end.toISOString() : start;
showEventDetails(info.event.id, start, end);
}
});
calendar.render();
$('#toggle-calendar').click(function() {
$('#calendar-container').toggle(); // Ein- und Ausblenden des Kalender-Containers
});
// Automatisch das erste Event anzeigen
setTimeout(() => {
var firstEvent = calendar.getEvents()[0];
if (firstEvent) {
var start = firstEvent.start.toISOString();
var end = firstEvent.end ? firstEvent.end.toISOString() : start;
showEventDetails(firstEvent.id, start, end);
}
}, 0);
function loadLatestEventDetails() {
$.ajax({
url: '/latest-event/',
method: 'GET',
success: function(result) {
if (!$.isEmptyObject(result)) {
updateEventDetails(result);
} else {
$('#event-details').html('<p>Keine bevorstehenden Events.</p>');
}
}
});
}
function showEventDetails(event_id, start, end) {
$.ajax({
url: '/kirtanloveberlin/get_event/' + event_id + '/?date=' + start.split('T')[0],
method: 'GET',
success: function(result) {
updateEventDetails(result);
}
});
}
function updateEventDetails(result) {
var startDate = new Date(result.start);
var endDate = new Date(result.end);
$('#event-details').html(`
<h2>${result.title}</h2>
<p><span class="bold">Start:</span> ${startDate.toLocaleDateString("de-DE")} ${startDate.toLocaleTimeString("de-DE", { hour: '2-digit', minute:'2-digit', hour12: false })}</p>
<p><span class="bold">End:</span> ${endDate.toLocaleDateString("de-DE")} ${endDate.toLocaleTimeString("de-DE", { hour: '2-digit', minute:'2-digit', hour12: false })}</p>
<p><span class="bold">Description:</span> ${result.description}</p>
<p><span class="bold">Comment:</span> ${result.comment}</p>
<p><span class="bold">Price:</span> ${result.price}</p>
<p><span class="bold">Location:</span> ${result.location}</p>
<p><span class="bold">Contact:</span> ${result.contact}</p>
<p><span class="bold">Google maps </span> <a href="${result.google_maps_link}">Link</a></p>
<p><span class="bold">Website:</span> <a href="${result.website}">${result.website}</a></p>
${result.event_image ? '<img src="'+result.event_image+'" class="event-image">' : ''}
`);
}
loadLatestEventDetails();
});
</script>
</body>
</html>
views.py
from django.shortcuts import render, Http404
from django.http import JsonResponse
from django.utils import timezone
from .models import CustomEvent
import json
from datetime import timedelta, datetime
from django.views.decorators.csrf import csrf_exempt
def calendar_view(request):
start_date = timezone.now()
end_date = start_date + timedelta(days=365)
events = CustomEvent.objects.all()
event_arr = []
for event in events:
occurrences = event.get_occurrences(start_date, end_date)
for occurrence in occurrences:
event_sub_arr = {}
event_sub_arr['id'] = event.id
event_sub_arr['title'] = event.title
event_sub_arr['start'] = str(occurrence.start)
event_sub_arr['end'] = str(occurrence.end)
event_sub_arr['event_image'] = event.event_image.url if event.event_image else None
event_sub_arr['google_maps_link'] = event.google_maps_link
event_arr.append(event_sub_arr)
return JsonResponse(event_arr, safe=False)
@csrf_exempt
def change_events(request):
if request.method == 'POST':
data = json.loads(request.body)
param = data['param']
event_id = data['id']
event = CustomEvent.objects.get(pk=event_id)
if param == 'start':
event.start = data['start']
else:
event.end = data['end']
event.save()
return JsonResponse({}, status=200)
def calendar_page_view(request):
latest_event = CustomEvent.objects.filter(end__gte=timezone.now()).order_by('end').first()
if latest_event:
latest_event_data = {
'id': latest_event.id,
'title': latest_event.title,
'start': str(latest_event.start),
'end': str(latest_event.end),
'description': latest_event.description,
'comment': latest_event.comment,
'price': latest_event.price,
'location': latest_event.location,
'google_maps_link': latest_event.google_maps_link,
'contact': latest_event.contact,
'website': latest_event.website,
'event_image': latest_event.event_image.url if latest_event.event_image else None,
}
else:
latest_event_data = None
return render(request, 'calendar_page.html', {'latest_event': latest_event_data})
def get_event(request, event_id):
try:
event = CustomEvent.objects.get(pk=event_id)
except CustomEvent.DoesNotExist:
raise Http404("Event does not exist")
occurrence_date_str = request.GET.get('date', '')
start = event.start
end = event.end
if occurrence_date_str:
occurrence_date = datetime.strptime(occurrence_date_str, "%Y-%m-%d").date()
start = datetime.combine(occurrence_date, start.time())
end = datetime.combine(occurrence_date, end.time())
event_data = {
'id': event.id,
'title': event.title,
'start': str(start),
'end': str(end),
'description': event.description,
'comment': event.comment,
'price': event.price,
'location': event.location,
'google_maps_link': event.google_maps_link,
'contact': event.contact,
'website': event.website,
'event_image': event.event_image.url if event.event_image else None,
}
return JsonResponse(event_data)
def latest_event_view(request):
latest_event = CustomEvent.objects.filter(end__gte=timezone.now()).order_by('end').first()
if latest_event:
data = {
'id': latest_event.id,
'title': latest_event.title,
'start': str(latest_event.start),
'end': str(latest_event.end),
'description': latest_event.description,
'comment': latest_event.comment,
'price': latest_event.price,
'location': latest_event.location,
'google_maps_link': latest_event.google_maps_link,
'contact': latest_event.contact,
'website': latest_event.website,
'event_image': latest_event.event_image.url if latest_event.event_image else None,
}
else:
data = {}
return JsonResponse(data)
models.py
from django.db import models
from schedule.models import Event
from django_resized import ResizedImageField
#import recurrence.fields
# class Termin(models.Model):
# title = models.CharField(max_length=100)
# start = models.DateTimeField()
# end = models.DateTimeField()
# recurrences = recurrence.fields.RecurrenceField()
class CustomEvent(Event):
event_image = ResizedImageField(size=[500, 300], quality=99, upload_to='event_images', blank=True, null=True)
comment = models.TextField(blank=True, null=True)
price = models.CharField(max_length=200, blank=True, null=True)
location = models.TextField(blank=True, null=True)
google_maps_link = models.URLField(max_length=200, blank=True, null=True)
contact = models.TextField(blank=True, null=True)
website = models.TextField(blank=True, null=True)
def __str__(self):
return self.title
Shell I provide more informations?