Files
2026-05-28 23:43:20 +02:00

148 lines
5.3 KiB
Python

"""
Vues de l'application LOGIS.
"""
import logging
import datetime
from django.conf import settings
from django.core.mail import send_mail
from django.http import JsonResponse
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.translation import gettext as _
from django.views.generic import ListView, DetailView, View
from .forms import ContactForm
from .models import Property, ContactRequest
logger = logging.getLogger(__name__)
class PropertyListView(ListView):
"""Page d'accueil — liste des biens immobiliers actifs."""
model = Property
template_name = 'index.html'
context_object_name = 'properties'
def get_queryset(self):
"""Retourne les biens actifs avec leurs relations préchargées."""
return (
Property.objects.filter(is_active=True)
.prefetch_related('photos', 'features')
.select_related('environment')
)
class PropertyDetailView(DetailView):
"""Page de détail d'un bien immobilier."""
model = Property
template_name = 'properties/detail.html'
context_object_name = 'property'
def get_queryset(self):
"""Retourne uniquement les biens actifs."""
return (
Property.objects.filter(is_active=True)
.prefetch_related('photos', 'rooms__photos', 'features')
.select_related('environment')
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
prop = self.object
context['contact_form'] = ContactForm(
initial={'property_id': prop.pk}
)
context['rooms_with_photos'] = prop.rooms.prefetch_related('photos').all()
context['general_photos'] = prop.photos.filter(room__isnull=True)
return context
class ContactView(View):
"""Endpoint AJAX pour le formulaire de contact."""
def post(self, request, *args, **kwargs):
# Accepte uniquement les requêtes AJAX
if not request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return JsonResponse({'success': False}, status=400)
form = ContactForm(request.POST)
if not form.is_valid():
return JsonResponse({'success': False, 'errors': form.errors})
# Vérification honeypot : si rempli, c'est un robot
if form.cleaned_data.get('website'):
return JsonResponse({'success': True})
# Limitation de débit : 5 minutes entre deux envois par session
last_contact = request.session.get('last_contact')
if last_contact:
try:
last_time = datetime.datetime.fromisoformat(last_contact)
if timezone.is_naive(last_time):
last_time = timezone.make_aware(last_time)
elapsed = (timezone.now() - last_time).total_seconds()
if elapsed < 300:
remaining = int((300 - elapsed) / 60) + 1
return JsonResponse({
'success': False,
'error': str(_(
'Merci de patienter encore %(min)s minute(s) avant d\'envoyer un nouveau message.'
) % {'min': remaining}),
})
except (ValueError, TypeError):
pass
# Récupération du bien concerné
property_obj = None
pid = form.cleaned_data.get('property_id')
if pid:
try:
property_obj = Property.objects.get(pk=pid, is_active=True)
except Property.DoesNotExist:
pass
# Enregistrement en base
contact = ContactRequest.objects.create(
property=property_obj,
name=form.cleaned_data['name'],
email=form.cleaned_data['email'],
phone=form.cleaned_data.get('phone', ''),
message=form.cleaned_data['message'],
ip_address=self._get_client_ip(request),
)
# Envoi de l'email à l'administrateur
try:
ctx = {'contact': contact, 'property': property_obj}
subject = render_to_string('emails/contact_subject.txt', ctx).strip()
body_text = render_to_string('emails/contact_body.txt', ctx)
body_html = render_to_string('emails/contact_body.html', ctx)
send_mail(
subject=subject,
message=body_text,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[settings.ADMIN_EMAIL],
html_message=body_html,
fail_silently=False,
)
except Exception as exc:
# L'email échoue mais le contact est enregistré en base
logger.error('Échec envoi email contact #%s : %s', contact.pk, exc)
request.session['last_contact'] = timezone.now().isoformat()
return JsonResponse({
'success': True,
'message': str(_('Votre message a bien été envoyé. Nous vous répondrons dans les plus brefs délais.')),
})
def _get_client_ip(self, request):
"""Extrait l'adresse IP réelle du client derrière un proxy."""
forwarded = request.META.get('HTTP_X_FORWARDED_FOR')
if forwarded:
return forwarded.split(',')[0].strip()
return request.META.get('REMOTE_ADDR')