django-hotels 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. django_hotels-0.1.0/AUTHORS +0 -0
  2. django_hotels-0.1.0/LICENSE +21 -0
  3. django_hotels-0.1.0/MANIFEST.in +3 -0
  4. django_hotels-0.1.0/PKG-INFO +114 -0
  5. django_hotels-0.1.0/README.md +63 -0
  6. django_hotels-0.1.0/django-hotels/__init__.py +0 -0
  7. django_hotels-0.1.0/django-hotels/asgi.py +11 -0
  8. django_hotels-0.1.0/django-hotels/urls.py +9 -0
  9. django_hotels-0.1.0/django-hotels/wsgi.py +13 -0
  10. django_hotels-0.1.0/django_hotels/__init__.py +3 -0
  11. django_hotels-0.1.0/django_hotels/admin.py +46 -0
  12. django_hotels-0.1.0/django_hotels/api/__init__.py +0 -0
  13. django_hotels-0.1.0/django_hotels/api/serializers.py +107 -0
  14. django_hotels-0.1.0/django_hotels/api/urls.py +30 -0
  15. django_hotels-0.1.0/django_hotels/api/views/__init__.py +0 -0
  16. django_hotels-0.1.0/django_hotels/api/views/booking.py +31 -0
  17. django_hotels-0.1.0/django_hotels/api/views/hotel.py +37 -0
  18. django_hotels-0.1.0/django_hotels/apps.py +9 -0
  19. django_hotels-0.1.0/django_hotels/choices.py +30 -0
  20. django_hotels-0.1.0/django_hotels/management/__init__.py +0 -0
  21. django_hotels-0.1.0/django_hotels/management/commands/__init__.py +0 -0
  22. django_hotels-0.1.0/django_hotels/management/commands/generate_hotels.py +69 -0
  23. django_hotels-0.1.0/django_hotels/managers.py +16 -0
  24. django_hotels-0.1.0/django_hotels/migrations/0001_initial.py +294 -0
  25. django_hotels-0.1.0/django_hotels/migrations/__init__.py +0 -0
  26. django_hotels-0.1.0/django_hotels/models.py +225 -0
  27. django_hotels-0.1.0/django_hotels/urls.py +12 -0
  28. django_hotels-0.1.0/django_hotels.egg-info/PKG-INFO +114 -0
  29. django_hotels-0.1.0/django_hotels.egg-info/SOURCES.txt +37 -0
  30. django_hotels-0.1.0/django_hotels.egg-info/dependency_links.txt +1 -0
  31. django_hotels-0.1.0/django_hotels.egg-info/entry_points.txt +2 -0
  32. django_hotels-0.1.0/django_hotels.egg-info/requires.txt +20 -0
  33. django_hotels-0.1.0/django_hotels.egg-info/top_level.txt +3 -0
  34. django_hotels-0.1.0/settings/__init__.py +0 -0
  35. django_hotels-0.1.0/settings/common.py +95 -0
  36. django_hotels-0.1.0/settings/test.py +2 -0
  37. django_hotels-0.1.0/setup.cfg +58 -0
  38. django_hotels-0.1.0/setup.py +90 -0
File without changes
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 The Python Packaging Authority
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ include README.md
2
+ include LICENSE
3
+ recursive-include django_hotels/static *
@@ -0,0 +1,114 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-hotels
3
+ Version: 0.1.0
4
+ Summary: A Django Rest API for managing hotels, room types, availability, and bookings.
5
+ Home-page: https://github.com/DestinationPak/django-hotels
6
+ Author: Awais Jibran
7
+ Author-email: awaisdar001@gmail.com
8
+ License: MIT
9
+ Keywords: Django hotels
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.11
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ License-File: AUTHORS
19
+ Requires-Dist: mysqlclient>=2.2.1
20
+ Requires-Dist: setuptools>=80.9.0
21
+ Requires-Dist: django-filter>=23.2
22
+ Requires-Dist: Django<6.0,>=5.2
23
+ Requires-Dist: djangorestframework<3.17,>=3.16
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest==9.1.1; extra == "dev"
26
+ Requires-Dist: docutils==0.23; extra == "dev"
27
+ Requires-Dist: pylint-plugin-utils==0.9.0; extra == "dev"
28
+ Requires-Dist: Faker==40.31.0; extra == "dev"
29
+ Requires-Dist: pre-commit==4.0.1; extra == "dev"
30
+ Requires-Dist: pytest-django==4.12.0; extra == "dev"
31
+ Requires-Dist: pylint==4.0.6; extra == "dev"
32
+ Requires-Dist: isort==8.0.1; extra == "dev"
33
+ Requires-Dist: factory-boy==3.3.3; extra == "dev"
34
+ Requires-Dist: pytest-cov==7.1.0; extra == "dev"
35
+ Requires-Dist: twine==6.2.0; extra == "dev"
36
+ Requires-Dist: wheel; extra == "dev"
37
+ Requires-Dist: pylint-django==2.8.0; extra == "dev"
38
+ Dynamic: author
39
+ Dynamic: author-email
40
+ Dynamic: classifier
41
+ Dynamic: description
42
+ Dynamic: description-content-type
43
+ Dynamic: home-page
44
+ Dynamic: keywords
45
+ Dynamic: license
46
+ Dynamic: license-file
47
+ Dynamic: provides-extra
48
+ Dynamic: requires-dist
49
+ Dynamic: requires-python
50
+ Dynamic: summary
51
+
52
+ # Django Hotels API
53
+
54
+ This is a Django REST API for managing and retrieving hotels, room types, availability, and
55
+ bookings.
56
+
57
+ This service is a sibling of [django-trips](https://github.com/awaisdar001/django-trips), and
58
+ is a core component of the [DestinationPak](https://destinationpak.com) project — a platform
59
+ designed to make exploring and booking adventures across Pakistan easier and more accessible.
60
+
61
+ ## Installation
62
+ Simply do:
63
+ ```bash
64
+ pip install django-hotels
65
+ ```
66
+
67
+ ## Usage
68
+ Add the app into your installed apps in your project's settings file.
69
+ ```
70
+ INSTALLED_APPS = [
71
+ ...
72
+ 'django_hotels',
73
+ ]
74
+ ```
75
+ ## Migrate
76
+ ```
77
+ python manage.py migrate
78
+ ```
79
+ Add the following to your root `urls.py` or to your desired file location.
80
+ ```
81
+ urlpatterns = [
82
+ ...
83
+ path('hotels/', include('django_hotels.urls')),
84
+ ]
85
+ ```
86
+ This mounts the whole app under your own chosen namespace (`hotels/` above - replace with
87
+ whatever prefix you like) with the lib's own `v1/` version underneath it, e.g.
88
+ `hotels/v1/hotels/`. The app versions itself independently of your project's own API version.
89
+
90
+ ## Domain model
91
+
92
+ ```
93
+ HotelOwner (the business/brand)
94
+ └── Hotel (one bookable property)
95
+ ├── HotelImage
96
+ └── HotelRoomType (a room category/tier)
97
+ └── HotelAvailability (a dated, priced, bookable instance)
98
+ └── HotelBooking (a guest or logged-in traveller's booking)
99
+ ```
100
+
101
+ `HotelOwner`/`Hotel` deliberately carry no login/auth/permission fields — this package is
102
+ tenancy-oblivious, the same way `django-trips` is. A consuming project owns the membership
103
+ layer (who may manage which `HotelOwner`), not this library.
104
+
105
+ ## Local development
106
+
107
+ All commands assume Docker (`make dev.up`, `make update_db`, `make test`) — see the Makefile
108
+ for the full command list (`make help`).
109
+
110
+ ## Generate demo data
111
+
112
+ ```
113
+ ./manage.py generate_hotels --batch_size=10
114
+ ```
@@ -0,0 +1,63 @@
1
+ # Django Hotels API
2
+
3
+ This is a Django REST API for managing and retrieving hotels, room types, availability, and
4
+ bookings.
5
+
6
+ This service is a sibling of [django-trips](https://github.com/awaisdar001/django-trips), and
7
+ is a core component of the [DestinationPak](https://destinationpak.com) project — a platform
8
+ designed to make exploring and booking adventures across Pakistan easier and more accessible.
9
+
10
+ ## Installation
11
+ Simply do:
12
+ ```bash
13
+ pip install django-hotels
14
+ ```
15
+
16
+ ## Usage
17
+ Add the app into your installed apps in your project's settings file.
18
+ ```
19
+ INSTALLED_APPS = [
20
+ ...
21
+ 'django_hotels',
22
+ ]
23
+ ```
24
+ ## Migrate
25
+ ```
26
+ python manage.py migrate
27
+ ```
28
+ Add the following to your root `urls.py` or to your desired file location.
29
+ ```
30
+ urlpatterns = [
31
+ ...
32
+ path('hotels/', include('django_hotels.urls')),
33
+ ]
34
+ ```
35
+ This mounts the whole app under your own chosen namespace (`hotels/` above - replace with
36
+ whatever prefix you like) with the lib's own `v1/` version underneath it, e.g.
37
+ `hotels/v1/hotels/`. The app versions itself independently of your project's own API version.
38
+
39
+ ## Domain model
40
+
41
+ ```
42
+ HotelOwner (the business/brand)
43
+ └── Hotel (one bookable property)
44
+ ├── HotelImage
45
+ └── HotelRoomType (a room category/tier)
46
+ └── HotelAvailability (a dated, priced, bookable instance)
47
+ └── HotelBooking (a guest or logged-in traveller's booking)
48
+ ```
49
+
50
+ `HotelOwner`/`Hotel` deliberately carry no login/auth/permission fields — this package is
51
+ tenancy-oblivious, the same way `django-trips` is. A consuming project owns the membership
52
+ layer (who may manage which `HotelOwner`), not this library.
53
+
54
+ ## Local development
55
+
56
+ All commands assume Docker (`make dev.up`, `make update_db`, `make test`) — see the Makefile
57
+ for the full command list (`make help`).
58
+
59
+ ## Generate demo data
60
+
61
+ ```
62
+ ./manage.py generate_hotels --batch_size=10
63
+ ```
File without changes
@@ -0,0 +1,11 @@
1
+ """
2
+ ASGI config for hotels project.
3
+ """
4
+
5
+ import os
6
+
7
+ from django.core.asgi import get_asgi_application
8
+
9
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings.common')
10
+
11
+ application = get_asgi_application()
@@ -0,0 +1,9 @@
1
+ """hotels URL Configuration"""
2
+ from django.conf.urls import include
3
+ from django.contrib import admin
4
+ from django.urls import path
5
+
6
+ urlpatterns = [
7
+ path('admin/', admin.site.urls),
8
+ path('', include('django_hotels.urls')),
9
+ ]
@@ -0,0 +1,13 @@
1
+ # pylint:disable=all
2
+
3
+ """
4
+ WSGI config for hotels project.
5
+ """
6
+
7
+ import os
8
+
9
+ from django.core.wsgi import get_wsgi_application
10
+
11
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.common")
12
+
13
+ application = get_wsgi_application()
@@ -0,0 +1,3 @@
1
+ # pylint: disable=invalid-name
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,46 @@
1
+ from django.contrib import admin
2
+
3
+ from django_hotels.models import (
4
+ Hotel,
5
+ HotelAvailability,
6
+ HotelBooking,
7
+ HotelImage,
8
+ HotelOwner,
9
+ HotelRoomType,
10
+ )
11
+
12
+
13
+ @admin.register(HotelOwner)
14
+ class HotelOwnerAdmin(admin.ModelAdmin):
15
+ list_display = ("name", "email", "verified", "is_active")
16
+ search_fields = ("name", "email")
17
+
18
+
19
+ @admin.register(Hotel)
20
+ class HotelAdmin(admin.ModelAdmin):
21
+ list_display = ("name", "owner", "city", "status", "is_active")
22
+ list_filter = ("status", "is_active", "city")
23
+ search_fields = ("name", "city")
24
+
25
+
26
+ @admin.register(HotelRoomType)
27
+ class HotelRoomTypeAdmin(admin.ModelAdmin):
28
+ list_display = ("hotel", "name", "base_price", "max_occupancy", "is_active")
29
+
30
+
31
+ @admin.register(HotelAvailability)
32
+ class HotelAvailabilityAdmin(admin.ModelAdmin):
33
+ list_display = ("room_type", "date", "effective_price", "rooms_available")
34
+ list_filter = ("date",)
35
+
36
+
37
+ @admin.register(HotelImage)
38
+ class HotelImageAdmin(admin.ModelAdmin):
39
+ list_display = ("hotel", "order")
40
+
41
+
42
+ @admin.register(HotelBooking)
43
+ class HotelBookingAdmin(admin.ModelAdmin):
44
+ list_display = ("number", "full_name", "email", "status", "created_at")
45
+ list_filter = ("status",)
46
+ search_fields = ("number", "full_name", "email")
File without changes
@@ -0,0 +1,107 @@
1
+ from rest_framework import serializers
2
+
3
+ from django_hotels.models import (
4
+ Hotel,
5
+ HotelAvailability,
6
+ HotelBooking,
7
+ HotelImage,
8
+ HotelOwner,
9
+ HotelRoomType,
10
+ )
11
+
12
+
13
+ class HotelOwnerSerializer(serializers.ModelSerializer):
14
+ class Meta:
15
+ model = HotelOwner
16
+ fields = (
17
+ "id",
18
+ "name",
19
+ "slug",
20
+ "description",
21
+ "email",
22
+ "mobile",
23
+ "verified",
24
+ )
25
+
26
+
27
+ class HotelImageSerializer(serializers.ModelSerializer):
28
+ class Meta:
29
+ model = HotelImage
30
+ fields = ("id", "image", "caption", "order")
31
+
32
+
33
+ class HotelRoomTypeSerializer(serializers.ModelSerializer):
34
+ class Meta:
35
+ model = HotelRoomType
36
+ fields = ("id", "name", "base_price", "max_occupancy")
37
+
38
+
39
+ class HotelListSerializer(serializers.ModelSerializer):
40
+ owner = HotelOwnerSerializer(read_only=True)
41
+
42
+ class Meta:
43
+ model = Hotel
44
+ fields = ("id", "name", "slug", "owner", "city", "status")
45
+
46
+
47
+ class HotelDetailSerializer(serializers.ModelSerializer):
48
+ owner = HotelOwnerSerializer(read_only=True)
49
+ images = HotelImageSerializer(many=True, read_only=True)
50
+ room_types = HotelRoomTypeSerializer(many=True, read_only=True)
51
+
52
+ class Meta:
53
+ model = Hotel
54
+ fields = (
55
+ "id",
56
+ "name",
57
+ "slug",
58
+ "owner",
59
+ "city",
60
+ "description",
61
+ "status",
62
+ "images",
63
+ "room_types",
64
+ )
65
+
66
+
67
+ class HotelAvailabilitySerializer(serializers.ModelSerializer):
68
+ class Meta:
69
+ model = HotelAvailability
70
+ fields = ("id", "room_type", "date", "effective_price", "rooms_available")
71
+
72
+
73
+ class HotelBookingCreateSerializer(serializers.ModelSerializer):
74
+ class Meta:
75
+ model = HotelBooking
76
+ fields = (
77
+ "id",
78
+ "number",
79
+ "availability",
80
+ "full_name",
81
+ "email",
82
+ "phone_number",
83
+ "guests",
84
+ "message",
85
+ "total_price",
86
+ )
87
+ read_only_fields = ("id", "number", "total_price")
88
+
89
+ def create(self, validated_data):
90
+ availability = validated_data["availability"]
91
+ guests = validated_data.get("guests", 1)
92
+ validated_data["total_price"] = availability.effective_price * guests
93
+ return super().create(validated_data)
94
+
95
+
96
+ class HotelBookingLookupSerializer(serializers.ModelSerializer):
97
+ class Meta:
98
+ model = HotelBooking
99
+ fields = (
100
+ "number",
101
+ "full_name",
102
+ "email",
103
+ "guests",
104
+ "status",
105
+ "total_price",
106
+ "created_at",
107
+ )
@@ -0,0 +1,30 @@
1
+ from django.urls import path
2
+ from rest_framework.routers import DefaultRouter
3
+
4
+ from django_hotels.api.views import booking, hotel
5
+
6
+ app_name = "hotels-api"
7
+
8
+ router = DefaultRouter()
9
+ router.register(r"hotels", hotel.HotelViewSet, basename="hotel")
10
+
11
+ app_urlpatterns = [
12
+ path(
13
+ "owners/",
14
+ hotel.ActiveHotelOwnersListAPIView.as_view(),
15
+ name="owners",
16
+ ),
17
+ path(
18
+ "bookings/create/",
19
+ booking.HotelBookingCreateView.as_view(),
20
+ name="bookings-create",
21
+ ),
22
+ path(
23
+ "bookings/lookup/",
24
+ booking.HotelBookingLookupView.as_view(),
25
+ name="bookings-lookup",
26
+ ),
27
+ *router.urls,
28
+ ]
29
+
30
+ urlpatterns = app_urlpatterns
@@ -0,0 +1,31 @@
1
+ from rest_framework import generics
2
+ from rest_framework.exceptions import NotFound
3
+ from rest_framework.permissions import AllowAny
4
+
5
+ from django_hotels.api.serializers import (
6
+ HotelBookingCreateSerializer,
7
+ HotelBookingLookupSerializer,
8
+ )
9
+ from django_hotels.models import HotelBooking
10
+
11
+
12
+ class HotelBookingCreateView(generics.CreateAPIView):
13
+ """Guest booking - no auth required, mirrors django-trips' TripBookingCreateView."""
14
+
15
+ permission_classes = [AllowAny]
16
+ serializer_class = HotelBookingCreateSerializer
17
+
18
+
19
+ class HotelBookingLookupView(generics.RetrieveAPIView):
20
+ """Anonymous booking lookup by `number` + `email` - never by number alone."""
21
+
22
+ permission_classes = [AllowAny]
23
+ serializer_class = HotelBookingLookupSerializer
24
+
25
+ def get_object(self):
26
+ number = self.request.query_params.get("number")
27
+ email = self.request.query_params.get("email")
28
+ booking = HotelBooking.objects.filter(number=number, email=email).first()
29
+ if not booking:
30
+ raise NotFound("No booking found for that reference number and email.")
31
+ return booking
@@ -0,0 +1,37 @@
1
+ from rest_framework import generics
2
+ from rest_framework.permissions import AllowAny
3
+ from rest_framework.viewsets import ReadOnlyModelViewSet
4
+
5
+ from django_hotels.api.serializers import (
6
+ HotelDetailSerializer,
7
+ HotelListSerializer,
8
+ HotelOwnerSerializer,
9
+ )
10
+ from django_hotels.models import Hotel, HotelOwner
11
+
12
+
13
+ class HotelViewSet(ReadOnlyModelViewSet):
14
+ """Public, read-only catalog of published hotels - no create/update/destroy.
15
+
16
+ Management (create/update/archive) is a consuming project's operator-surface
17
+ job, the same architectural split django-trips uses for TripViewSet.
18
+ """
19
+
20
+ permission_classes = [AllowAny]
21
+ lookup_field = "slug"
22
+
23
+ def get_queryset(self):
24
+ return Hotel.objects.active().select_related("owner")
25
+
26
+ def get_serializer_class(self):
27
+ if self.action == "retrieve":
28
+ return HotelDetailSerializer
29
+ return HotelListSerializer
30
+
31
+
32
+ class ActiveHotelOwnersListAPIView(generics.ListAPIView):
33
+ permission_classes = [AllowAny]
34
+ serializer_class = HotelOwnerSerializer
35
+
36
+ def get_queryset(self):
37
+ return HotelOwner.objects.active()
@@ -0,0 +1,9 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class DjangoHotelsConfig(AppConfig):
5
+ name = "django_hotels"
6
+ verbose_name = "Django Hotels"
7
+ # Pinned per-app so this package's models always get BigAutoField
8
+ # regardless of the consuming project's own DEFAULT_AUTO_FIELD.
9
+ default_auto_field = "django.db.models.BigAutoField"
@@ -0,0 +1,30 @@
1
+ from django.db import models
2
+
3
+
4
+ class HotelStatus(models.TextChoices):
5
+ """
6
+ Editorial state of a Hotel, independent of `is_active` (which is
7
+ soft-delete/visibility, not workflow).
8
+ """
9
+
10
+ DRAFT = "DRAFT", "Draft"
11
+ PUBLISHED = "PUBLISHED", "Published"
12
+
13
+
14
+ class HotelBookingStatus(models.TextChoices):
15
+ """Lifecycle states of a booking - mirrors django_trips.choices.BookingStatus."""
16
+
17
+ PENDING = "PENDING", "Pending"
18
+ WAITING_PAYMENT = "WAITING_PAYMENT", "Awaiting Payment"
19
+ CONFIRMED = "CONFIRMED", "Confirmed"
20
+ PARTIAL_PAYMENT = "PARTIAL_PAYMENT", "Partial Payment"
21
+ COMPLETED = "COMPLETED", "Completed"
22
+ CANCELLED = "CANCELLED", "Cancelled"
23
+
24
+ @classmethod
25
+ def is_cancelled(cls, status):
26
+ return status == cls.CANCELLED
27
+
28
+ @classmethod
29
+ def can_be_cancelled(cls, status):
30
+ return status in (cls.PENDING, cls.WAITING_PAYMENT)
@@ -0,0 +1,69 @@
1
+ import random
2
+ from datetime import timedelta
3
+
4
+ from django.contrib.auth import get_user_model
5
+ from django.core.management.base import BaseCommand
6
+ from django.utils import timezone
7
+ from django.utils.text import slugify
8
+ from faker import Faker
9
+
10
+ from django_hotels.models import Hotel, HotelAvailability, HotelOwner, HotelRoomType
11
+
12
+ fake = Faker()
13
+ User = get_user_model()
14
+
15
+ CITIES = ("Hunza", "Skardu", "Naran", "Murree", "Fairy Meadows")
16
+ ROOM_TYPES = (("Standard Double", 8000, 2), ("Deluxe Suite", 15000, 4))
17
+
18
+
19
+ class Command(BaseCommand):
20
+ """
21
+ Generate a batch of hotels with random demo data.
22
+
23
+ EXAMPLE USAGE:
24
+ ./manage.py generate_hotels --batch_size=10
25
+ """
26
+
27
+ help = "Generate batches of hotels with pre-populated random data"
28
+
29
+ def add_arguments(self, parser):
30
+ parser.add_argument("--batch_size", type=int, default=10, dest="batch_size")
31
+
32
+ def handle(self, *args, **options):
33
+ batch_size = options["batch_size"]
34
+ user, _ = User.objects.get_or_create(
35
+ username="hotels-seed-bot", defaults={"email": "seed@example.com"}
36
+ )
37
+
38
+ for _ in range(batch_size):
39
+ owner_name = fake.company()
40
+ owner = HotelOwner.objects.create(
41
+ name=owner_name,
42
+ slug=f"{slugify(owner_name)}-{random.randint(1000, 9999)}",
43
+ email=fake.company_email(),
44
+ verified=True,
45
+ )
46
+ hotel_name = f"{fake.city()} {random.choice(['Inn', 'Resort', 'Lodge'])}"
47
+ hotel = Hotel.objects.create(
48
+ name=hotel_name,
49
+ slug=f"{slugify(hotel_name)}-{random.randint(1000, 9999)}",
50
+ owner=owner,
51
+ city=random.choice(CITIES),
52
+ description=fake.paragraph(),
53
+ created_by=user,
54
+ )
55
+ for room_name, price, occupancy in ROOM_TYPES:
56
+ room_type = HotelRoomType.objects.create(
57
+ hotel=hotel,
58
+ name=room_name,
59
+ base_price=price,
60
+ max_occupancy=occupancy,
61
+ )
62
+ for day in range(7):
63
+ HotelAvailability.objects.create(
64
+ room_type=room_type,
65
+ date=timezone.now().date() + timedelta(days=day),
66
+ rooms_available=random.randint(1, 5),
67
+ )
68
+
69
+ self.stdout.write(self.style.SUCCESS(f"Generated {batch_size} hotels"))
@@ -0,0 +1,16 @@
1
+ from django.db import models
2
+
3
+
4
+ class ActiveQuerySet(models.QuerySet):
5
+ def active(self):
6
+ return self.filter(is_active=True)
7
+
8
+
9
+ class HotelOwnerQuerySet(ActiveQuerySet):
10
+ def active(self):
11
+ return super().active().filter(verified=True)
12
+
13
+
14
+ class HotelQuerySet(ActiveQuerySet):
15
+ def active(self):
16
+ return super().active().filter(owner__verified=True)