From 58c1936d7c2a2f68bea72cc11c0cd75acb46e224 Mon Sep 17 00:00:00 2001 From: "jean-baptiste.guerin" Date: Fri, 9 May 2025 15:20:49 +0200 Subject: [PATCH 01/14] chore: update requirements --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c48c84ea40..13e02d0e33 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -django==3.0 +django==4.2.11 flake8==3.7.0 pytest-django==3.9.0 \ No newline at end of file From 5f3d48e895f76f93ef4655fd7e797d2aaf5f23e5 Mon Sep 17 00:00:00 2001 From: "jean-baptiste.guerin" Date: Fri, 16 May 2025 14:40:16 +0200 Subject: [PATCH 02/14] refactor: clean db --- oc-lettings-site.sqlite3 | Bin 151552 -> 180224 bytes ...ng_address_remove_profile_user_and_more.py | 30 ++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 oc_lettings_site/migrations/0002_remove_letting_address_remove_profile_user_and_more.py diff --git a/oc-lettings-site.sqlite3 b/oc-lettings-site.sqlite3 index 3d885414f9f3ed046704e8b09bdcd5c791c82d42..8258bc7c2e30cc9e0e0eaa83ecda1f82af1c3ba9 100644 GIT binary patch delta 3126 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 diff --git a/oc_lettings_site/migrations/0002_remove_letting_address_remove_profile_user_and_more.py b/oc_lettings_site/migrations/0002_remove_letting_address_remove_profile_user_and_more.py new file mode 100644 index 0000000000..ec34d47d68 --- /dev/null +++ b/oc_lettings_site/migrations/0002_remove_letting_address_remove_profile_user_and_more.py @@ -0,0 +1,30 @@ +# Generated by Django 4.2.11 on 2025-05-16 12:00 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('oc_lettings_site', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='letting', + name='address', + ), + migrations.RemoveField( + model_name='profile', + name='user', + ), + migrations.DeleteModel( + name='Address', + ), + migrations.DeleteModel( + name='Letting', + ), + migrations.DeleteModel( + name='Profile', + ), + ] From a2660da0d69a1e6f8e3dc08a3e11d8354b8f2cd1 Mon Sep 17 00:00:00 2001 From: "jean-baptiste.guerin" Date: Fri, 16 May 2025 14:40:38 +0200 Subject: [PATCH 03/14] refactor: lettings app --- lettings/__init__.py | 0 lettings/admin.py | 6 +++ lettings/apps.py | 6 +++ lettings/migrations/0001_initial.py | 36 +++++++++++++++ .../0002_copy_data_from_old_models.py | 37 +++++++++++++++ lettings/migrations/__init__.py | 0 lettings/models.py | 22 +++++++++ lettings/templates/lettings/index.html | 45 +++++++++++++++++++ lettings/templates/lettings/letting.html | 41 +++++++++++++++++ lettings/tests.py | 3 ++ lettings/urls.py | 9 ++++ lettings/views.py | 15 +++++++ 12 files changed, 220 insertions(+) create mode 100644 lettings/__init__.py create mode 100644 lettings/admin.py create mode 100644 lettings/apps.py create mode 100644 lettings/migrations/0001_initial.py create mode 100644 lettings/migrations/0002_copy_data_from_old_models.py create mode 100644 lettings/migrations/__init__.py create mode 100644 lettings/models.py create mode 100644 lettings/templates/lettings/index.html create mode 100644 lettings/templates/lettings/letting.html create mode 100644 lettings/tests.py create mode 100644 lettings/urls.py create mode 100644 lettings/views.py diff --git a/lettings/__init__.py b/lettings/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lettings/admin.py b/lettings/admin.py new file mode 100644 index 0000000000..acd55bc056 --- /dev/null +++ b/lettings/admin.py @@ -0,0 +1,6 @@ +from django.contrib import admin +from .models import Letting, Address + +# Register your models here. +admin.site.register(Letting) +admin.site.register(Address) diff --git a/lettings/apps.py b/lettings/apps.py new file mode 100644 index 0000000000..63dada00c4 --- /dev/null +++ b/lettings/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class LettingsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'lettings' diff --git a/lettings/migrations/0001_initial.py b/lettings/migrations/0001_initial.py new file mode 100644 index 0000000000..bd7d8ded63 --- /dev/null +++ b/lettings/migrations/0001_initial.py @@ -0,0 +1,36 @@ +# Generated by Django 4.2.11 on 2025-05-16 11:59 + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Address', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('number', models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(9999)])), + ('street', models.CharField(max_length=64)), + ('city', models.CharField(max_length=64)), + ('state', models.CharField(max_length=2, validators=[django.core.validators.MinLengthValidator(2)])), + ('zip_code', models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(99999)])), + ('country_iso_code', models.CharField(max_length=3, validators=[django.core.validators.MinLengthValidator(3)])), + ], + ), + migrations.CreateModel( + name='Letting', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=256)), + ('address', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='lettings.address')), + ], + ), + ] diff --git a/lettings/migrations/0002_copy_data_from_old_models.py b/lettings/migrations/0002_copy_data_from_old_models.py new file mode 100644 index 0000000000..c70d60b390 --- /dev/null +++ b/lettings/migrations/0002_copy_data_from_old_models.py @@ -0,0 +1,37 @@ +from django.db import migrations + +def copy_lettings_and_addresses(apps, schema_editor): + OldAddress = apps.get_model('oc_lettings_site', 'Address') + OldLetting = apps.get_model('oc_lettings_site', 'Letting') + NewAddress = apps.get_model('lettings', 'Address') + NewLetting = apps.get_model('lettings', 'Letting') + + # Copier les adresses + old_to_new_address = {} + for old_addr in OldAddress.objects.all(): + new_addr = NewAddress.objects.create( + number=old_addr.number, + street=old_addr.street, + city=old_addr.city, + state=old_addr.state, + zip_code=old_addr.zip_code, + country_iso_code=old_addr.country_iso_code, + ) + old_to_new_address[old_addr.id] = new_addr + + # Copier les lettings + for old_letting in OldLetting.objects.all(): + NewLetting.objects.create( + title=old_letting.title, + address=old_to_new_address.get(old_letting.address_id) + ) + +class Migration(migrations.Migration): + dependencies = [ + ('lettings', '0001_initial'), + ('oc_lettings_site', '__first__'), + ] + + operations = [ + migrations.RunPython(copy_lettings_and_addresses), + ] \ No newline at end of file diff --git a/lettings/migrations/__init__.py b/lettings/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lettings/models.py b/lettings/models.py new file mode 100644 index 0000000000..8e76ef00d2 --- /dev/null +++ b/lettings/models.py @@ -0,0 +1,22 @@ +from django.db import models +from django.core.validators import MaxValueValidator, MinLengthValidator + + +class Address(models.Model): + number = models.PositiveIntegerField(validators=[MaxValueValidator(9999)]) + street = models.CharField(max_length=64) + city = models.CharField(max_length=64) + state = models.CharField(max_length=2, validators=[MinLengthValidator(2)]) + zip_code = models.PositiveIntegerField(validators=[MaxValueValidator(99999)]) + country_iso_code = models.CharField(max_length=3, validators=[MinLengthValidator(3)]) + + def __str__(self): + return f'{self.number} {self.street}' + + +class Letting(models.Model): + title = models.CharField(max_length=256) + address = models.OneToOneField(Address, on_delete=models.CASCADE) + + def __str__(self): + return self.title diff --git a/lettings/templates/lettings/index.html b/lettings/templates/lettings/index.html new file mode 100644 index 0000000000..0251067b6b --- /dev/null +++ b/lettings/templates/lettings/index.html @@ -0,0 +1,45 @@ +{% extends "base.html" %} +{% block title %}Lettings{% endblock title %} + +{% block content %} + + +
+
+
+

Lettings

+
+
+
+ +
+
+
+
+ {% if lettings_list %} + + {% else %} +

No lettings are available.

+ {% endif %} +
+
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/lettings/templates/lettings/letting.html b/lettings/templates/lettings/letting.html new file mode 100644 index 0000000000..3a050e03cc --- /dev/null +++ b/lettings/templates/lettings/letting.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} +{% load static %} +{% block title %}{{ title }}{% endblock title %} + +{% block content %} + +
+
+
+

{{ title }}

+
+
+
+ +
+
+
+
+

{{ address.number }} {{ address.street }}

+

{{ address.city }}, {{ address.state }} {{ address.zip_code }}

+

{{ address.country_iso_code }}

+
+
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/lettings/tests.py b/lettings/tests.py new file mode 100644 index 0000000000..7ce503c2dd --- /dev/null +++ b/lettings/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/lettings/urls.py b/lettings/urls.py new file mode 100644 index 0000000000..f86b5ec42f --- /dev/null +++ b/lettings/urls.py @@ -0,0 +1,9 @@ +from django.urls import path +from . import views + +app_name = 'lettings' + +urlpatterns = [ + path('', views.index, name='index'), + path('/', views.letting, name='letting'), +] \ No newline at end of file diff --git a/lettings/views.py b/lettings/views.py new file mode 100644 index 0000000000..f1a4845edd --- /dev/null +++ b/lettings/views.py @@ -0,0 +1,15 @@ +from django.shortcuts import render, get_object_or_404 +from .models import Letting + +def index(request): + lettings_list = Letting.objects.all() + context = {'lettings_list': lettings_list} + return render(request, 'lettings/index.html', context) + +def letting(request, letting_id): + letting = get_object_or_404(Letting, id=letting_id) + context = { + 'title': letting.title, + 'address': letting.address, + } + return render(request, 'lettings/letting.html', context) From 051a32c9a35003240ffbc5c5f48ae0cc084dff64 Mon Sep 17 00:00:00 2001 From: "jean-baptiste.guerin" Date: Fri, 16 May 2025 14:40:57 +0200 Subject: [PATCH 04/14] refactor: profiles app --- profiles/__init__.py | 0 profiles/admin.py | 5 +++ profiles/apps.py | 6 +++ profiles/migrations/0001_initial.py | 25 +++++++++++ .../0002_copy_data_from_old_models.py | 22 ++++++++++ profiles/migrations/__init__.py | 0 profiles/models.py | 10 +++++ profiles/templates/profiles/index.html | 43 +++++++++++++++++++ profiles/templates/profiles/profile.html | 40 +++++++++++++++++ profiles/tests.py | 3 ++ profiles/urls.py | 9 ++++ profiles/views.py | 12 ++++++ 12 files changed, 175 insertions(+) create mode 100644 profiles/__init__.py create mode 100644 profiles/admin.py create mode 100644 profiles/apps.py create mode 100644 profiles/migrations/0001_initial.py create mode 100644 profiles/migrations/0002_copy_data_from_old_models.py create mode 100644 profiles/migrations/__init__.py create mode 100644 profiles/models.py create mode 100644 profiles/templates/profiles/index.html create mode 100644 profiles/templates/profiles/profile.html create mode 100644 profiles/tests.py create mode 100644 profiles/urls.py create mode 100644 profiles/views.py diff --git a/profiles/__init__.py b/profiles/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/profiles/admin.py b/profiles/admin.py new file mode 100644 index 0000000000..123dd95de0 --- /dev/null +++ b/profiles/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin +from .models import Profile + +# Register your models here. +admin.site.register(Profile) diff --git a/profiles/apps.py b/profiles/apps.py new file mode 100644 index 0000000000..d722976462 --- /dev/null +++ b/profiles/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ProfilesConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'profiles' diff --git a/profiles/migrations/0001_initial.py b/profiles/migrations/0001_initial.py new file mode 100644 index 0000000000..fae9ad6bb1 --- /dev/null +++ b/profiles/migrations/0001_initial.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.11 on 2025-05-16 11:59 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Profile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('favorite_city', models.CharField(blank=True, max_length=64)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/profiles/migrations/0002_copy_data_from_old_models.py b/profiles/migrations/0002_copy_data_from_old_models.py new file mode 100644 index 0000000000..2338da9b98 --- /dev/null +++ b/profiles/migrations/0002_copy_data_from_old_models.py @@ -0,0 +1,22 @@ +from django.db import migrations + +def copy_profiles(apps, schema_editor): + OldProfile = apps.get_model('oc_lettings_site', 'Profile') + NewProfile = apps.get_model('profiles', 'Profile') + User = apps.get_model('auth', 'User') + + for old_profile in OldProfile.objects.all(): + NewProfile.objects.create( + user=User.objects.get(pk=old_profile.user_id), + favorite_city=old_profile.favorite_city, + ) + +class Migration(migrations.Migration): + dependencies = [ + ('profiles', '0001_initial'), + ('oc_lettings_site', '__first__'), + ] + + operations = [ + migrations.RunPython(copy_profiles), + ] \ No newline at end of file diff --git a/profiles/migrations/__init__.py b/profiles/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/profiles/models.py b/profiles/models.py new file mode 100644 index 0000000000..84c85c1001 --- /dev/null +++ b/profiles/models.py @@ -0,0 +1,10 @@ +from django.db import models +from django.contrib.auth.models import User + + +class Profile(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE) + favorite_city = models.CharField(max_length=64, blank=True) + + def __str__(self): + return self.user.username diff --git a/profiles/templates/profiles/index.html b/profiles/templates/profiles/index.html new file mode 100644 index 0000000000..09ee8df3ec --- /dev/null +++ b/profiles/templates/profiles/index.html @@ -0,0 +1,43 @@ +{% extends "base.html" %} +{% block title %}Profiles{% endblock title %} + +{% block content %} +
+
+
+

Profiles

+
+
+
+ +
+
+
+
+ {% if profiles_list %} + + {% else %} +

No profiles are available.

+ {% endif %} +
+
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/profiles/templates/profiles/profile.html b/profiles/templates/profiles/profile.html new file mode 100644 index 0000000000..c87f86d5f2 --- /dev/null +++ b/profiles/templates/profiles/profile.html @@ -0,0 +1,40 @@ +{% extends "base.html" %} +{% block title %}{{ profile.user.username }}{% endblock title %} + +{% block content %} +
+
+
+

{{ profile.user.username }}

+
+
+
+ +
+
+
+
+

First name : {{ profile.user.first_name }}

+

Last name : {{ profile.user.last_name }}

+

Email : {{ profile.user.email }}

+

Favorite city : {{ profile.favorite_city }}

+
+
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/profiles/tests.py b/profiles/tests.py new file mode 100644 index 0000000000..7ce503c2dd --- /dev/null +++ b/profiles/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/profiles/urls.py b/profiles/urls.py new file mode 100644 index 0000000000..e97f355941 --- /dev/null +++ b/profiles/urls.py @@ -0,0 +1,9 @@ +from django.urls import path +from . import views + +app_name = 'profiles' + +urlpatterns = [ + path('', views.index, name='index'), + path('/', views.profile, name='profile'), +] \ No newline at end of file diff --git a/profiles/views.py b/profiles/views.py new file mode 100644 index 0000000000..e9ffd6d7d9 --- /dev/null +++ b/profiles/views.py @@ -0,0 +1,12 @@ +from django.shortcuts import render, get_object_or_404 +from .models import Profile + +def index(request): + profiles_list = Profile.objects.all() + context = {'profiles_list': profiles_list} + return render(request, 'profiles/index.html', context) + +def profile(request, username): + profile = get_object_or_404(Profile, user__username=username) + context = {'profile': profile} + return render(request, 'profiles/profile.html', context) From 048abb99ed8592e7ca1b1fd07cee2ecf89e3f9f8 Mon Sep 17 00:00:00 2001 From: "jean-baptiste.guerin" Date: Fri, 16 May 2025 14:41:33 +0200 Subject: [PATCH 05/14] refactor: templates --- .../templates}/base.html | 6 +-- .../templates}/index.html | 6 +-- templates/letting.html | 41 ----------------- templates/lettings_index.html | 45 ------------------- 4 files changed, 6 insertions(+), 92 deletions(-) rename {templates => oc_lettings_site/templates}/base.html (97%) rename {templates => oc_lettings_site/templates}/index.html (90%) delete mode 100644 templates/letting.html delete mode 100644 templates/lettings_index.html diff --git a/templates/base.html b/oc_lettings_site/templates/base.html similarity index 97% rename from templates/base.html rename to oc_lettings_site/templates/base.html index ab7addba01..a480792d39 100644 --- a/templates/base.html +++ b/oc_lettings_site/templates/base.html @@ -24,10 +24,10 @@
Logo Orange County Lettings @@ -64,4 +64,4 @@ }); - \ No newline at end of file + \ No newline at end of file diff --git a/templates/index.html b/oc_lettings_site/templates/index.html similarity index 90% rename from templates/index.html rename to oc_lettings_site/templates/index.html index 71a8e61a46..cf7f8d8122 100644 --- a/templates/index.html +++ b/oc_lettings_site/templates/index.html @@ -14,13 +14,13 @@

Welcome to Holiday Homes

-{% endblock %} \ No newline at end of file +{% endblock %} \ No newline at end of file diff --git a/templates/letting.html b/templates/letting.html deleted file mode 100644 index 7e5f3a73fd..0000000000 --- a/templates/letting.html +++ /dev/null @@ -1,41 +0,0 @@ -{% extends "base.html" %} -{% load static %} -{% block title %}{{ title }}{% endblock title %} - -{% block content %} - -
-
-
-

{{ title }}

-
-
-
- -
-
-
-
-

{{ address.number }} {{ address.street }}

-

{{ address.city }}, {{ address.state }} {{ address.zip_code }}

-

{{ address.country_iso_code }}

-
-
-
- - - -{% endblock %} \ No newline at end of file diff --git a/templates/lettings_index.html b/templates/lettings_index.html deleted file mode 100644 index 92857a78d9..0000000000 --- a/templates/lettings_index.html +++ /dev/null @@ -1,45 +0,0 @@ -{% extends "base.html" %} -{% block title %}Lettings{% endblock title %} - -{% block content %} - - -
-
-
-

Lettings

-
-
-
- -
-
-
-
- {% if lettings_list %} - - {% else %} -

No lettings are available.

- {% endif %} -
-
-
- - - -{% endblock %} \ No newline at end of file From f5b2c25d0de663c7e1d8030f0fa0b7591d863be0 Mon Sep 17 00:00:00 2001 From: "jean-baptiste.guerin" Date: Fri, 16 May 2025 14:41:50 +0200 Subject: [PATCH 06/14] refactor: main app --- oc_lettings_site/admin.py | 10 +-------- oc_lettings_site/models.py | 32 +--------------------------- oc_lettings_site/settings.py | 4 +++- oc_lettings_site/urls.py | 9 +++----- oc_lettings_site/views.py | 41 ------------------------------------ 5 files changed, 8 insertions(+), 88 deletions(-) diff --git a/oc_lettings_site/admin.py b/oc_lettings_site/admin.py index 63328c6dd3..d29977c6bb 100644 --- a/oc_lettings_site/admin.py +++ b/oc_lettings_site/admin.py @@ -1,10 +1,2 @@ from django.contrib import admin - -from .models import Letting -from .models import Address -from .models import Profile - - -admin.site.register(Letting) -admin.site.register(Address) -admin.site.register(Profile) +# Les modèles ont été déplacés dans les applications 'lettings' et 'profiles'. diff --git a/oc_lettings_site/models.py b/oc_lettings_site/models.py index ed255e8c11..65bb1527fd 100644 --- a/oc_lettings_site/models.py +++ b/oc_lettings_site/models.py @@ -1,31 +1 @@ -from django.db import models -from django.core.validators import MaxValueValidator, MinLengthValidator -from django.contrib.auth.models import User - - -class Address(models.Model): - number = models.PositiveIntegerField(validators=[MaxValueValidator(9999)]) - street = models.CharField(max_length=64) - city = models.CharField(max_length=64) - state = models.CharField(max_length=2, validators=[MinLengthValidator(2)]) - zip_code = models.PositiveIntegerField(validators=[MaxValueValidator(99999)]) - country_iso_code = models.CharField(max_length=3, validators=[MinLengthValidator(3)]) - - def __str__(self): - return f'{self.number} {self.street}' - - -class Letting(models.Model): - title = models.CharField(max_length=256) - address = models.OneToOneField(Address, on_delete=models.CASCADE) - - def __str__(self): - return self.title - - -class Profile(models.Model): - user = models.OneToOneField(User, on_delete=models.CASCADE) - favorite_city = models.CharField(max_length=64, blank=True) - - def __str__(self): - return self.user.username +# Les modèles ont été déplacés dans les applications 'lettings' et 'profiles'. diff --git a/oc_lettings_site/settings.py b/oc_lettings_site/settings.py index a18bee8106..ac2ec49c90 100644 --- a/oc_lettings_site/settings.py +++ b/oc_lettings_site/settings.py @@ -28,6 +28,8 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'lettings', + 'profiles', ] MIDDLEWARE = [ @@ -45,7 +47,7 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, 'templates')], + 'DIRS': [os.path.join(BASE_DIR, 'oc_lettings_site/templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ diff --git a/oc_lettings_site/urls.py b/oc_lettings_site/urls.py index f0ff5897ab..71d2e86c5a 100644 --- a/oc_lettings_site/urls.py +++ b/oc_lettings_site/urls.py @@ -1,13 +1,10 @@ from django.contrib import admin -from django.urls import path - +from django.urls import path, include from . import views urlpatterns = [ path('', views.index, name='index'), - path('lettings/', views.lettings_index, name='lettings_index'), - path('lettings//', views.letting, name='letting'), - path('profiles/', views.profiles_index, name='profiles_index'), - path('profiles//', views.profile, name='profile'), + path('lettings/', include('lettings.urls', namespace='lettings')), + path('profiles/', include('profiles.urls', namespace='profiles')), path('admin/', admin.site.urls), ] diff --git a/oc_lettings_site/views.py b/oc_lettings_site/views.py index a72db27074..cbd3ce2411 100644 --- a/oc_lettings_site/views.py +++ b/oc_lettings_site/views.py @@ -1,45 +1,4 @@ from django.shortcuts import render -from .models import Letting, Profile - - - -# Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque molestie quam lobortis leo consectetur ullamcorper non id est. Praesent dictum, nulla eget feugiat sagittis, sem mi convallis eros, -# vitae dapibus nisi lorem dapibus sem. Maecenas pharetra purus ipsum, eget consequat ipsum lobortis quis. Phasellus eleifend ex auctor venenatis tempus. -# Aliquam vitae erat ac orci placerat luctus. Nullam elementum urna nisi, pellentesque iaculis enim cursus in. Praesent volutpat porttitor magna, non finibus neque cursus id. def index(request): return render(request, 'index.html') - -# Aenean leo magna, vestibulum et tincidunt fermentum, consectetur quis velit. Sed non placerat massa. Integer est nunc, pulvinar a -# tempor et, bibendum id arcu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Cras eget scelerisque -def lettings_index(request): - lettings_list = Letting.objects.all() - context = {'lettings_list': lettings_list} - return render(request, 'lettings_index.html', context) - - -#Cras ultricies dignissim purus, vitae hendrerit ex varius non. In accumsan porta nisl id eleifend. Praesent dignissim, odio eu consequat pretium, purus urna vulputate arcu, vitae efficitur -# lacus justo nec purus. Aenean finibus faucibus lectus at porta. Maecenas auctor, est ut luctus congue, dui enim mattis enim, ac condimentum velit libero in magna. Suspendisse potenti. In tempus a nisi sed laoreet. -# Suspendisse porta dui eget sem accumsan interdum. Ut quis urna pellentesque justo mattis ullamcorper ac non tellus. In tristique mauris eu velit fermentum, tempus pharetra est luctus. Vivamus consequat aliquam libero, eget bibendum lorem. Sed non dolor risus. Mauris condimentum auctor elementum. Donec quis nisi ligula. Integer vehicula tincidunt enim, ac lacinia augue pulvinar sit amet. -def letting(request, letting_id): - letting = Letting.objects.get(id=letting_id) - context = { - 'title': letting.title, - 'address': letting.address, - } - return render(request, 'letting.html', context) - -# Sed placerat quam in pulvinar commodo. Nullam laoreet consectetur ex, sed consequat libero pulvinar eget. Fusc -# faucibus, urna quis auctor pharetra, massa dolor cursus neque, quis dictum lacus d -def profiles_index(request): - profiles_list = Profile.objects.all() - context = {'profiles_list': profiles_list} - return render(request, 'profiles_index.html', context) - -# Aliquam sed metus eget nisi tincidunt ornare accumsan eget lac -# laoreet neque quis, pellentesque dui. Nullam facilisis pharetra vulputate. Sed tincidunt, dolor id facilisis fringilla, eros leo tristique lacus, -# it. Nam aliquam dignissim congue. Pellentesque habitant morbi tristique senectus et netus et males -def profile(request, username): - profile = Profile.objects.get(user__username=username) - context = {'profile': profile} - return render(request, 'profile.html', context) From d84c8c7ea2bfd12a580e755667a6eef2d3a34c2e Mon Sep 17 00:00:00 2001 From: "jean-baptiste.guerin" Date: Fri, 23 May 2025 15:00:36 +0200 Subject: [PATCH 07/14] feat: error 404 and 500 templates --- .../migrations/0002_copy_data_from_old_models.py | 1 - oc_lettings_site/settings.py | 2 +- oc_lettings_site/templates/404.html | 12 ++++++++++++ oc_lettings_site/templates/500.html | 13 +++++++++++++ oc_lettings_site/urls.py | 8 +++++++- oc_lettings_site/views.py | 9 +++++++++ 6 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 oc_lettings_site/templates/404.html create mode 100644 oc_lettings_site/templates/500.html diff --git a/lettings/migrations/0002_copy_data_from_old_models.py b/lettings/migrations/0002_copy_data_from_old_models.py index c70d60b390..fd16f02d90 100644 --- a/lettings/migrations/0002_copy_data_from_old_models.py +++ b/lettings/migrations/0002_copy_data_from_old_models.py @@ -6,7 +6,6 @@ def copy_lettings_and_addresses(apps, schema_editor): NewAddress = apps.get_model('lettings', 'Address') NewLetting = apps.get_model('lettings', 'Letting') - # Copier les adresses old_to_new_address = {} for old_addr in OldAddress.objects.all(): new_addr = NewAddress.objects.create( diff --git a/oc_lettings_site/settings.py b/oc_lettings_site/settings.py index ac2ec49c90..d00ad365ac 100644 --- a/oc_lettings_site/settings.py +++ b/oc_lettings_site/settings.py @@ -15,7 +15,7 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ['localhost', '127.0.0.1'] # Application definition diff --git a/oc_lettings_site/templates/404.html b/oc_lettings_site/templates/404.html new file mode 100644 index 0000000000..4d71daac0c --- /dev/null +++ b/oc_lettings_site/templates/404.html @@ -0,0 +1,12 @@ +{% extends 'base.html' %} + +{% block title %}Page non trouvée{% endblock %} + +{% block content %} +
+

404

+

Oops !

+

La page que vous recherchez n'existe pas.

+ Retour à l'accueil +
+{% endblock %} diff --git a/oc_lettings_site/templates/500.html b/oc_lettings_site/templates/500.html new file mode 100644 index 0000000000..725094beac --- /dev/null +++ b/oc_lettings_site/templates/500.html @@ -0,0 +1,13 @@ +{% extends 'base.html' %} + +{% block title %}Erreur serveur{% endblock %} + +{% block content %} +
+

500

+

Oops !

+

Une erreur inattendue s'est produite.

+

Notre équipe technique a été notifiée et travaille à résoudre le problème.

+ Retour à l'accueil +
+{% endblock %} diff --git a/oc_lettings_site/urls.py b/oc_lettings_site/urls.py index 71d2e86c5a..6395d2933e 100644 --- a/oc_lettings_site/urls.py +++ b/oc_lettings_site/urls.py @@ -1,5 +1,8 @@ from django.contrib import admin from django.urls import path, include +from django.conf import settings +from django.conf.urls.static import static + from . import views urlpatterns = [ @@ -7,4 +10,7 @@ path('lettings/', include('lettings.urls', namespace='lettings')), path('profiles/', include('profiles.urls', namespace='profiles')), path('admin/', admin.site.urls), -] + # Test URLs for error pages + path('404/', views.test_404, name='test_404'), + path('500/', views.test_500, name='test_500'), +] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/oc_lettings_site/views.py b/oc_lettings_site/views.py index cbd3ce2411..508217a5d2 100644 --- a/oc_lettings_site/views.py +++ b/oc_lettings_site/views.py @@ -1,4 +1,13 @@ from django.shortcuts import render +from django.http import Http404 def index(request): return render(request, 'index.html') + +def test_404(request): + raise Http404("Page de test 404") + +def test_500(request): + # Forcer une erreur 500 en divisant par zéro + division_by_zero = 1 / 0 + return render(request, 'index.html') From 18276800e270defc1e4ddc8475e6122175c0e7d4 Mon Sep 17 00:00:00 2001 From: "jean-baptiste.guerin" Date: Fri, 23 May 2025 15:14:13 +0200 Subject: [PATCH 08/14] docs:add docstings --- lettings/models.py | 20 ++++++++++++++++++++ lettings/views.py | 21 +++++++++++++++++++++ oc_lettings_site/views.py | 28 +++++++++++++++++++++++++++- profiles/models.py | 12 ++++++++++++ profiles/views.py | 22 ++++++++++++++++++++++ 5 files changed, 102 insertions(+), 1 deletion(-) diff --git a/lettings/models.py b/lettings/models.py index 8e76ef00d2..4baab75e43 100644 --- a/lettings/models.py +++ b/lettings/models.py @@ -3,6 +3,12 @@ class Address(models.Model): + """ + Model representing a physical address. + + This model stores : + number, street, city, state, zip code, and country code. + """ number = models.PositiveIntegerField(validators=[MaxValueValidator(9999)]) street = models.CharField(max_length=64) city = models.CharField(max_length=64) @@ -11,12 +17,26 @@ class Address(models.Model): country_iso_code = models.CharField(max_length=3, validators=[MinLengthValidator(3)]) def __str__(self): + """ + Returns a string representation of the address. + + Returns: + str: The address in the format 'number street' + """ return f'{self.number} {self.street}' class Letting(models.Model): + """ + Model representing a property letting. + + This model stores title and associated address. + """ title = models.CharField(max_length=256) address = models.OneToOneField(Address, on_delete=models.CASCADE) def __str__(self): + """ + Returns the title of the letting. + """ return self.title diff --git a/lettings/views.py b/lettings/views.py index f1a4845edd..d23516c266 100644 --- a/lettings/views.py +++ b/lettings/views.py @@ -2,11 +2,32 @@ from .models import Letting def index(request): + """ + View function for the lettings list page. + + Args: + request: The HTTP request object + Returns: + template 'lettings/index.html' with a list of all lettings + """ lettings_list = Letting.objects.all() context = {'lettings_list': lettings_list} return render(request, 'lettings/index.html', context) def letting(request, letting_id): + """ + View function for displaying a specific letting's details. + + Args: + request: The HTTP request object + letting_id: The ID of the letting to display + + Returns: + template 'lettings/letting.html' with the requested letting's details + + Raises: + Http404: If the letting with the given ID doesn't exist + """ letting = get_object_or_404(Letting, id=letting_id) context = { 'title': letting.title, diff --git a/oc_lettings_site/views.py b/oc_lettings_site/views.py index 508217a5d2..47341afde5 100644 --- a/oc_lettings_site/views.py +++ b/oc_lettings_site/views.py @@ -2,12 +2,38 @@ from django.http import Http404 def index(request): + """ + View function for the home page. + + Args: + request: The HTTP request object + + Returns: + Rendered template 'index.html' + """ return render(request, 'index.html') def test_404(request): + """ + Test view that raises a 404 error. + + Args: + request: The HTTP request object + + Raises: + Http404: Always raises a 404 error for testing purposes + """ raise Http404("Page de test 404") def test_500(request): - # Forcer une erreur 500 en divisant par zéro + """ + Test view that raises a 500 error by performing a division by zero. + + Args: + request: The HTTP request object + + Returns: + This function never returns as it raises a ZeroDivisionError + """ division_by_zero = 1 / 0 return render(request, 'index.html') diff --git a/profiles/models.py b/profiles/models.py index 84c85c1001..84d34c12cb 100644 --- a/profiles/models.py +++ b/profiles/models.py @@ -3,8 +3,20 @@ class Profile(models.Model): + """ + Model representing a user profile. + + This model extends the default Django User model with additional information + about the user's favorite city. + """ user = models.OneToOneField(User, on_delete=models.CASCADE) favorite_city = models.CharField(max_length=64, blank=True) def __str__(self): + """ + Returns the username of the associated user. + + Returns: + str: The username of the user + """ return self.user.username diff --git a/profiles/views.py b/profiles/views.py index e9ffd6d7d9..6688410fee 100644 --- a/profiles/views.py +++ b/profiles/views.py @@ -2,11 +2,33 @@ from .models import Profile def index(request): + """ + View function for the profiles list page. + + Args: + request: The HTTP request object + + Returns: + Rendered template 'profiles/index.html' with a list of all profiles + """ profiles_list = Profile.objects.all() context = {'profiles_list': profiles_list} return render(request, 'profiles/index.html', context) def profile(request, username): + """ + View function for displaying a specific user's profile. + + Args: + request: The HTTP request object + username: The username of the profile to display + + Returns: + Rendered template 'profiles/profile.html' with the requested profile + + Raises: + Http404: If the profile with the given username doesn't exist + """ profile = get_object_or_404(Profile, user__username=username) context = {'profile': profile} return render(request, 'profiles/profile.html', context) From cf670d1e89769a7378ad20698a0d18726b6e72ad Mon Sep 17 00:00:00 2001 From: "jean-baptiste.guerin" Date: Fri, 23 May 2025 15:37:01 +0200 Subject: [PATCH 09/14] chore: clean code --- lettings/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lettings/models.py b/lettings/models.py index 4baab75e43..0a507536ff 100644 --- a/lettings/models.py +++ b/lettings/models.py @@ -30,7 +30,7 @@ class Letting(models.Model): """ Model representing a property letting. - This model stores title and associated address. + This model stores title and associated addresses. """ title = models.CharField(max_length=256) address = models.OneToOneField(Address, on_delete=models.CASCADE) From e55deac6934f15810ccd604158eb7c1beccc95de Mon Sep 17 00:00:00 2001 From: "jean-baptiste.guerin" Date: Mon, 26 May 2025 11:03:16 +0200 Subject: [PATCH 10/14] chore: clean code --- lettings/models.py | 3 --- lettings/urls.py | 2 +- lettings/views.py | 8 +++----- oc_lettings_site/settings.py | 2 +- oc_lettings_site/views.py | 9 +++------ profiles/models.py | 2 -- profiles/urls.py | 2 +- profiles/views.py | 7 ++----- 8 files changed, 11 insertions(+), 24 deletions(-) diff --git a/lettings/models.py b/lettings/models.py index 0a507536ff..c4abb499f0 100644 --- a/lettings/models.py +++ b/lettings/models.py @@ -5,7 +5,6 @@ class Address(models.Model): """ Model representing a physical address. - This model stores : number, street, city, state, zip code, and country code. """ @@ -19,7 +18,6 @@ class Address(models.Model): def __str__(self): """ Returns a string representation of the address. - Returns: str: The address in the format 'number street' """ @@ -29,7 +27,6 @@ def __str__(self): class Letting(models.Model): """ Model representing a property letting. - This model stores title and associated addresses. """ title = models.CharField(max_length=256) diff --git a/lettings/urls.py b/lettings/urls.py index f86b5ec42f..b2f0174c74 100644 --- a/lettings/urls.py +++ b/lettings/urls.py @@ -6,4 +6,4 @@ urlpatterns = [ path('', views.index, name='index'), path('/', views.letting, name='letting'), -] \ No newline at end of file +] diff --git a/lettings/views.py b/lettings/views.py index d23516c266..c9c4caf556 100644 --- a/lettings/views.py +++ b/lettings/views.py @@ -1,12 +1,12 @@ from django.shortcuts import render, get_object_or_404 from .models import Letting + def index(request): """ View function for the lettings list page. - Args: - request: The HTTP request object + request: The HTTP request object Returns: template 'lettings/index.html' with a list of all lettings """ @@ -14,17 +14,15 @@ def index(request): context = {'lettings_list': lettings_list} return render(request, 'lettings/index.html', context) + def letting(request, letting_id): """ View function for displaying a specific letting's details. - Args: request: The HTTP request object letting_id: The ID of the letting to display - Returns: template 'lettings/letting.html' with the requested letting's details - Raises: Http404: If the letting with the given ID doesn't exist """ diff --git a/oc_lettings_site/settings.py b/oc_lettings_site/settings.py index d00ad365ac..be98636d2a 100644 --- a/oc_lettings_site/settings.py +++ b/oc_lettings_site/settings.py @@ -113,4 +113,4 @@ STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') STATIC_URL = '/static/' -STATICFILES_DIRS = [BASE_DIR / "static",] +STATICFILES_DIRS = [BASE_DIR / "static"] diff --git a/oc_lettings_site/views.py b/oc_lettings_site/views.py index 47341afde5..7962fef08b 100644 --- a/oc_lettings_site/views.py +++ b/oc_lettings_site/views.py @@ -1,37 +1,34 @@ from django.shortcuts import render from django.http import Http404 + def index(request): """ View function for the home page. - Args: request: The HTTP request object - Returns: Rendered template 'index.html' """ return render(request, 'index.html') + def test_404(request): """ Test view that raises a 404 error. - Args: request: The HTTP request object - Raises: Http404: Always raises a 404 error for testing purposes """ raise Http404("Page de test 404") + def test_500(request): """ Test view that raises a 500 error by performing a division by zero. - Args: request: The HTTP request object - Returns: This function never returns as it raises a ZeroDivisionError """ diff --git a/profiles/models.py b/profiles/models.py index 84d34c12cb..3dfee9cae8 100644 --- a/profiles/models.py +++ b/profiles/models.py @@ -5,7 +5,6 @@ class Profile(models.Model): """ Model representing a user profile. - This model extends the default Django User model with additional information about the user's favorite city. """ @@ -15,7 +14,6 @@ class Profile(models.Model): def __str__(self): """ Returns the username of the associated user. - Returns: str: The username of the user """ diff --git a/profiles/urls.py b/profiles/urls.py index e97f355941..9a6d0ee8e3 100644 --- a/profiles/urls.py +++ b/profiles/urls.py @@ -6,4 +6,4 @@ urlpatterns = [ path('', views.index, name='index'), path('/', views.profile, name='profile'), -] \ No newline at end of file +] diff --git a/profiles/views.py b/profiles/views.py index 6688410fee..96ccf09ed9 100644 --- a/profiles/views.py +++ b/profiles/views.py @@ -1,13 +1,12 @@ from django.shortcuts import render, get_object_or_404 from .models import Profile + def index(request): """ View function for the profiles list page. - Args: request: The HTTP request object - Returns: Rendered template 'profiles/index.html' with a list of all profiles """ @@ -15,17 +14,15 @@ def index(request): context = {'profiles_list': profiles_list} return render(request, 'profiles/index.html', context) + def profile(request, username): """ View function for displaying a specific user's profile. - Args: request: The HTTP request object username: The username of the profile to display - Returns: Rendered template 'profiles/profile.html' with the requested profile - Raises: Http404: If the profile with the given username doesn't exist """ From 483a9bc1453f96a6da33e776fe971ea6f455830d Mon Sep 17 00:00:00 2001 From: "jean-baptiste.guerin" Date: Fri, 30 May 2025 12:31:57 +0200 Subject: [PATCH 11/14] feat: coverage-tests --- .coverage | Bin 0 -> 53248 bytes .gitignore | 1 + Makefile | 33 +++++++++++ lettings/tests.py | 52 +++++++++++++++++- oc_lettings_site/tests.py | 31 +++++++++++ .../0002_copy_data_from_old_models.py | 22 -------- profiles/tests.py | 50 ++++++++++++++++- 7 files changed, 163 insertions(+), 26 deletions(-) create mode 100644 .coverage create mode 100644 Makefile delete mode 100644 profiles/migrations/0002_copy_data_from_old_models.py diff --git a/.coverage b/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..ed0d4d7fac4599a6a3a71e972aa662f129729af7 GIT binary patch literal 53248 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 bfCP{L5 Date: Fri, 30 May 2025 13:36:32 +0200 Subject: [PATCH 12/14] feat: pluralisation --- lettings/models.py | 6 ++++++ profiles/models.py | 3 +++ 2 files changed, 9 insertions(+) diff --git a/lettings/models.py b/lettings/models.py index c4abb499f0..84f1d51f36 100644 --- a/lettings/models.py +++ b/lettings/models.py @@ -15,6 +15,9 @@ class Address(models.Model): zip_code = models.PositiveIntegerField(validators=[MaxValueValidator(99999)]) country_iso_code = models.CharField(max_length=3, validators=[MinLengthValidator(3)]) + class Meta: + verbose_name_plural = "Addresses" + def __str__(self): """ Returns a string representation of the address. @@ -32,6 +35,9 @@ class Letting(models.Model): title = models.CharField(max_length=256) address = models.OneToOneField(Address, on_delete=models.CASCADE) + class Meta: + verbose_name_plural = "Lettings" + def __str__(self): """ Returns the title of the letting. diff --git a/profiles/models.py b/profiles/models.py index 3dfee9cae8..9ac020da62 100644 --- a/profiles/models.py +++ b/profiles/models.py @@ -11,6 +11,9 @@ class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) favorite_city = models.CharField(max_length=64, blank=True) + class Meta: + verbose_name_plural = "Profiles" + def __str__(self): """ Returns the username of the associated user. From 6e85e04cc183db2803d8dc4036f55d7dcb63978f Mon Sep 17 00:00:00 2001 From: "jean-baptiste.guerin" Date: Sat, 31 May 2025 19:10:24 +0200 Subject: [PATCH 13/14] chore:clean code flake8 --- lettings/tests.py | 5 ----- oc_lettings_site/admin.py | 1 - oc_lettings_site/tests.py | 6 +++--- oc_lettings_site/views.py | 2 +- profiles/tests.py | 10 +++++----- 5 files changed, 9 insertions(+), 15 deletions(-) diff --git a/lettings/tests.py b/lettings/tests.py index 6e4b835821..dcbbcc52de 100644 --- a/lettings/tests.py +++ b/lettings/tests.py @@ -24,7 +24,6 @@ def test_index_view(self): """Test the index view.""" url = reverse('lettings:index') response = self.client.get(url) - self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'lettings/index.html') self.assertIn('lettings_list', response.context) @@ -33,10 +32,8 @@ def test_index_view(self): def test_letting_view(self): """Test the letting detail view.""" - url = reverse('lettings:letting', args=[self.letting.id]) response = self.client.get(url) - self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'lettings/letting.html') self.assertEqual(response.context['title'], self.letting.title) @@ -44,8 +41,6 @@ def test_letting_view(self): def test_letting_view_404(self): """Test the letting detail view with non-existent letting.""" - url = reverse('lettings:letting', args=[999]) response = self.client.get(url) - self.assertEqual(response.status_code, 404) diff --git a/oc_lettings_site/admin.py b/oc_lettings_site/admin.py index d29977c6bb..65bb1527fd 100644 --- a/oc_lettings_site/admin.py +++ b/oc_lettings_site/admin.py @@ -1,2 +1 @@ -from django.contrib import admin # Les modèles ont été déplacés dans les applications 'lettings' et 'profiles'. diff --git a/oc_lettings_site/tests.py b/oc_lettings_site/tests.py index 2884106360..656ffe7894 100644 --- a/oc_lettings_site/tests.py +++ b/oc_lettings_site/tests.py @@ -1,6 +1,5 @@ from django.test import TestCase, Client from django.urls import reverse -from django.http import Http404 class OCLettingsSiteViewsTest(TestCase): @@ -12,14 +11,14 @@ def test_index_view(self): """Test the index view.""" url = reverse('index') response = self.client.get(url) - + self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'index.html') def test_404_view(self): """Test the 404 error view.""" url = reverse('test_404') - + response = self.client.get(url) self.assertEqual(response.status_code, 404) @@ -29,5 +28,6 @@ def test_500_view(self): with self.assertRaises(ZeroDivisionError): self.client.get(url) + def test_dummy(): assert 1 diff --git a/oc_lettings_site/views.py b/oc_lettings_site/views.py index 7962fef08b..c96cdfa7ce 100644 --- a/oc_lettings_site/views.py +++ b/oc_lettings_site/views.py @@ -32,5 +32,5 @@ def test_500(request): Returns: This function never returns as it raises a ZeroDivisionError """ - division_by_zero = 1 / 0 + 1 / 0 # Raise ZeroDivisionError return render(request, 'index.html') diff --git a/profiles/tests.py b/profiles/tests.py index 6d83498abf..2a66ccf7dc 100644 --- a/profiles/tests.py +++ b/profiles/tests.py @@ -21,10 +21,10 @@ def setUp(self): def test_index_view(self): """Test the profiles list view.""" - + url = reverse('profiles:index') response = self.client.get(url) - + self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'profiles/index.html') self.assertIn('profiles_list', response.context) @@ -33,17 +33,17 @@ def test_index_view(self): def test_profile_view(self): """Test the profile detail view.""" - + url = reverse('profiles:profile', args=[self.user.username]) response = self.client.get(url) - + self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'profiles/profile.html') self.assertEqual(response.context['profile'], self.profile) def test_profile_view_404(self): """Test the profile detail view with non-existent username.""" - + url = reverse('profiles:profile', args=['nonexistentuser']) response = self.client.get(url) self.assertEqual(response.status_code, 404) From f002ed781ca0d0c82a9d7542c06a758615d1847f Mon Sep 17 00:00:00 2001 From: "jean-baptiste.guerin" Date: Sat, 31 May 2025 19:47:49 +0200 Subject: [PATCH 14/14] feat:init sentry --- .gitignore | 1 + lettings/views.py | 33 ++++++++++++++++------- oc_lettings_site/settings.py | 52 +++++++++++++++++++++++++++++++++++- oc_lettings_site/urls.py | 6 +++++ oc_lettings_site/views.py | 12 +++++++-- profiles/views.py | 27 ++++++++++++++----- 6 files changed, 111 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index 64e2ff4e2a..47b985badd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.pyc venv .DS_Store +.env \ No newline at end of file diff --git a/lettings/views.py b/lettings/views.py index c9c4caf556..b7a4e2c9f9 100644 --- a/lettings/views.py +++ b/lettings/views.py @@ -1,6 +1,9 @@ from django.shortcuts import render, get_object_or_404 +from django.http import Http404 +import logging from .models import Letting +logger = logging.getLogger(__name__) def index(request): """ @@ -10,10 +13,13 @@ def index(request): Returns: template 'lettings/index.html' with a list of all lettings """ - lettings_list = Letting.objects.all() - context = {'lettings_list': lettings_list} - return render(request, 'lettings/index.html', context) - + try: + lettings_list = Letting.objects.all() + logger.info(f"Retrieved {len(lettings_list)} lettings for index page") + return render(request, 'lettings/index.html', context={'lettings_list': lettings_list}) + except Exception as e: + logger.error(f"Error retrieving lettings list: {str(e)}", exc_info=True) + raise def letting(request, letting_id): """ @@ -26,9 +32,16 @@ def letting(request, letting_id): Raises: Http404: If the letting with the given ID doesn't exist """ - letting = get_object_or_404(Letting, id=letting_id) - context = { - 'title': letting.title, - 'address': letting.address, - } - return render(request, 'lettings/letting.html', context) + try: + letting = get_object_or_404(Letting, id=letting_id) + logger.info(f"Retrieved letting {letting_id}: {letting.title}") + return render(request, 'lettings/letting.html', context={ + 'title': letting.title, + 'address': letting.address, + }) + except Http404: + logger.warning(f"Letting with id {letting_id} not found") + raise + except Exception as e: + logger.error(f"Error retrieving letting {letting_id}: {str(e)}", exc_info=True) + raise diff --git a/oc_lettings_site/settings.py b/oc_lettings_site/settings.py index be98636d2a..e6202e2c49 100644 --- a/oc_lettings_site/settings.py +++ b/oc_lettings_site/settings.py @@ -1,10 +1,60 @@ import os - from pathlib import Path +import sentry_sdk +from sentry_sdk.integrations.django import DjangoIntegration +from sentry_sdk.integrations.logging import LoggingIntegration +import logging + +sentry_sdk.init( + dsn = "https://7220363d53b6d31ec048180b5b0074e9@o452613.ingest.us.sentry.io/4509418921263104", + integrations=[ + DjangoIntegration(), + LoggingIntegration( + level=logging.INFO, + event_level=logging.ERROR + ), + ], + traces_sample_rate=1.0, + send_default_pii=True, +) # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = Path(__file__).resolve().parent.parent +# Logging config +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'verbose': { + 'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}', + 'style': '{', + }, + }, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + 'formatter': 'verbose', + }, + }, + 'loggers': { + 'django': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': True, + }, + 'lettings': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': True, + }, + 'profiles': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': True, + }, + }, +} # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ diff --git a/oc_lettings_site/urls.py b/oc_lettings_site/urls.py index 6395d2933e..7bfd70bd58 100644 --- a/oc_lettings_site/urls.py +++ b/oc_lettings_site/urls.py @@ -5,6 +5,9 @@ from . import views +def trigger_error(request): + division_by_zero = 1 / 0 + urlpatterns = [ path('', views.index, name='index'), path('lettings/', include('lettings.urls', namespace='lettings')), @@ -13,4 +16,7 @@ # Test URLs for error pages path('404/', views.test_404, name='test_404'), path('500/', views.test_500, name='test_500'), + # Test sentry + path('sentry-debug/', trigger_error), + ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/oc_lettings_site/views.py b/oc_lettings_site/views.py index c96cdfa7ce..1579bf3f6b 100644 --- a/oc_lettings_site/views.py +++ b/oc_lettings_site/views.py @@ -1,6 +1,8 @@ from django.shortcuts import render from django.http import Http404 +import logging +logger = logging.getLogger(__name__) def index(request): """ @@ -10,8 +12,12 @@ def index(request): Returns: Rendered template 'index.html' """ - return render(request, 'index.html') - + try: + logger.info("Rendering home page") + return render(request, 'index.html') + except Exception as e: + logger.error(f"Error rendering home page: {str(e)}", exc_info=True) + raise def test_404(request): """ @@ -21,6 +27,7 @@ def test_404(request): Raises: Http404: Always raises a 404 error for testing purposes """ + logger.warning("Test 404 error page requested") raise Http404("Page de test 404") @@ -32,5 +39,6 @@ def test_500(request): Returns: This function never returns as it raises a ZeroDivisionError """ + logger.error("Test 500 error page requested") 1 / 0 # Raise ZeroDivisionError return render(request, 'index.html') diff --git a/profiles/views.py b/profiles/views.py index 96ccf09ed9..2eb48c33cb 100644 --- a/profiles/views.py +++ b/profiles/views.py @@ -1,6 +1,9 @@ from django.shortcuts import render, get_object_or_404 +from django.http import Http404 +import logging from .models import Profile +logger = logging.getLogger(__name__) def index(request): """ @@ -10,10 +13,13 @@ def index(request): Returns: Rendered template 'profiles/index.html' with a list of all profiles """ - profiles_list = Profile.objects.all() - context = {'profiles_list': profiles_list} - return render(request, 'profiles/index.html', context) - + try: + profiles_list = Profile.objects.all() + logger.info(f"Retrieved {len(profiles_list)} profiles for index page") + return render(request, 'profiles/index.html', context={'profiles_list': profiles_list}) + except Exception as e: + logger.error(f"Error retrieving profiles list: {str(e)}", exc_info=True) + raise def profile(request, username): """ @@ -26,6 +32,13 @@ def profile(request, username): Raises: Http404: If the profile with the given username doesn't exist """ - profile = get_object_or_404(Profile, user__username=username) - context = {'profile': profile} - return render(request, 'profiles/profile.html', context) + try: + profile = get_object_or_404(Profile, user__username=username) + logger.info(f"Retrieved profile for user {username}") + return render(request, 'profiles/profile.html', context={'profile': profile}) + except Http404: + logger.warning(f"Profile for user {username} not found") + raise + except Exception as e: + logger.error(f"Error retrieving profile for user {username}: {str(e)}", exc_info=True) + raise