diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc new file mode 100644 index 0000000..905f5aa Binary files /dev/null and b/core/__pycache__/forms.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 0103a73..68e407f 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 85d8d1a..17981d3 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index f0a068a..5eca40a 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/forms.py b/core/forms.py new file mode 100644 index 0000000..c66d85a --- /dev/null +++ b/core/forms.py @@ -0,0 +1,16 @@ +from django import forms +from .models import Property, Booking + +class PropertyForm(forms.ModelForm): + class Meta: + model = Property + fields = ['title', 'location', 'price_per_night', 'image'] + +class BookingForm(forms.ModelForm): + class Meta: + model = Booking + fields = ['start_date', 'end_date'] + widgets = { + 'start_date': forms.DateInput(attrs={'type': 'date'}), + 'end_date': forms.DateInput(attrs={'type': 'date'}), + } diff --git a/core/migrations/0002_remove_property_image_url_property_image_and_more.py b/core/migrations/0002_remove_property_image_url_property_image_and_more.py new file mode 100644 index 0000000..32b340d --- /dev/null +++ b/core/migrations/0002_remove_property_image_url_property_image_and_more.py @@ -0,0 +1,30 @@ +# Generated by Django 5.2.7 on 2025-10-31 12:33 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.RemoveField( + model_name='property', + name='image_url', + ), + migrations.AddField( + model_name='property', + name='image', + field=models.ImageField(blank=True, null=True, upload_to='property_images/'), + ), + migrations.AddField( + model_name='property', + name='owner', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/core/migrations/0003_booking.py b/core/migrations/0003_booking.py new file mode 100644 index 0000000..7c5b6a9 --- /dev/null +++ b/core/migrations/0003_booking.py @@ -0,0 +1,26 @@ +# Generated by Django 5.2.7 on 2025-10-31 12:33 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0002_remove_property_image_url_property_image_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Booking', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('start_date', models.DateField()), + ('end_date', models.DateField()), + ('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.property')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/core/migrations/__pycache__/0002_remove_property_image_url_property_image_and_more.cpython-311.pyc b/core/migrations/__pycache__/0002_remove_property_image_url_property_image_and_more.cpython-311.pyc new file mode 100644 index 0000000..f986b50 Binary files /dev/null and b/core/migrations/__pycache__/0002_remove_property_image_url_property_image_and_more.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0003_booking.cpython-311.pyc b/core/migrations/__pycache__/0003_booking.cpython-311.pyc new file mode 100644 index 0000000..9307ff0 Binary files /dev/null and b/core/migrations/__pycache__/0003_booking.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index c1985ec..31144dd 100644 --- a/core/models.py +++ b/core/models.py @@ -1,10 +1,21 @@ from django.db import models +from django.contrib.auth.models import User class Property(models.Model): + owner = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True) title = models.CharField(max_length=255) location = models.CharField(max_length=255) price_per_night = models.DecimalField(max_digits=8, decimal_places=2) - image_url = models.URLField(max_length=1024, blank=True, null=True) + image = models.ImageField(upload_to='property_images/', blank=True, null=True) def __str__(self): - return self.title \ No newline at end of file + return self.title + +class Booking(models.Model): + property = models.ForeignKey(Property, on_delete=models.CASCADE) + user = models.ForeignKey(User, on_delete=models.CASCADE) + start_date = models.DateField() + end_date = models.DateField() + + def __str__(self): + return f'{self.user.username} - {self.property.title}' \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index bffb148..ed1d99a 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -13,7 +13,73 @@ {% block head %}{% endblock %} + + {% block content %}{% endblock %} + + + diff --git a/core/templates/core/about.html b/core/templates/core/about.html new file mode 100644 index 0000000..9890afb --- /dev/null +++ b/core/templates/core/about.html @@ -0,0 +1,10 @@ +{% extends 'base.html' %} + +{% block content %} +
+

About Us

+

Welcome to Homey, your number one source for vacation rentals. We're dedicated to giving you the very best of properties, with a focus on dependability, customer service and uniqueness.

+

Founded in 2025, Homey has come a long way from its beginnings. We now serve customers all over the world, and are thrilled to be a part of the quirky, eco-friendly, fair trade wing of the tourism industry.

+

We hope you enjoy our properties as much as we enjoy offering them to you. If you have any questions or comments, please don't hesitate to contact us.

+
+{% endblock %} diff --git a/core/templates/core/booking_create.html b/core/templates/core/booking_create.html new file mode 100644 index 0000000..63c0ac7 --- /dev/null +++ b/core/templates/core/booking_create.html @@ -0,0 +1,12 @@ +{% extends 'base.html' %} + +{% block content %} +
+

Book {{ property.title }}

+
+ {% csrf_token %} + {{ form.as_p }} + +
+
+{% endblock %} diff --git a/core/templates/core/contact.html b/core/templates/core/contact.html new file mode 100644 index 0000000..fbae91b --- /dev/null +++ b/core/templates/core/contact.html @@ -0,0 +1,9 @@ +{% extends 'base.html' %} + +{% block content %} +
+

Contact Us

+

If you have any questions, please feel free to contact us.

+

Email: contact@homey.com

+
+{% endblock %} diff --git a/core/templates/core/faq.html b/core/templates/core/faq.html new file mode 100644 index 0000000..c9942c5 --- /dev/null +++ b/core/templates/core/faq.html @@ -0,0 +1,16 @@ +{% extends 'base.html' %} + +{% block content %} +
+

Frequently Asked Questions

+ +

How do I book a property?

+

To book a property, simply click the "Book Now" button on the property's detail page and fill out the booking form.

+ +

Can I cancel a booking?

+

Currently, cancellation is not supported through the website. Please contact us directly to cancel a booking.

+ +

How do I list my own property?

+

After creating an account, you can click on the "Create Listing" link in the navigation bar to add your property.

+
+{% endblock %} diff --git a/core/templates/core/index.html b/core/templates/core/index.html index c86290d..7a14653 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -40,7 +40,7 @@
{{ property.title }}

{{ property.location }}

${{ property.price_per_night }} / night

- View Details + View Details diff --git a/core/templates/core/login.html b/core/templates/core/login.html new file mode 100644 index 0000000..38fc307 --- /dev/null +++ b/core/templates/core/login.html @@ -0,0 +1,22 @@ +{% extends 'base.html' %} + +{% block content %} +
+
+
+
+
+

Login

+
+
+
+ {% csrf_token %} + {{ form.as_p }} + +
+
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/profile.html b/core/templates/core/profile.html new file mode 100644 index 0000000..55d09af --- /dev/null +++ b/core/templates/core/profile.html @@ -0,0 +1,20 @@ +{% extends 'base.html' %} + +{% block content %} +
+

My Bookings

+ {% if bookings %} + + {% else %} +

You have no bookings yet.

+ {% endif %} +
+{% endblock %} diff --git a/core/templates/core/property_create.html b/core/templates/core/property_create.html new file mode 100644 index 0000000..8c9a251 --- /dev/null +++ b/core/templates/core/property_create.html @@ -0,0 +1,12 @@ +{% extends 'base.html' %} + +{% block content %} +
+

Create a New Property Listing

+
+ {% csrf_token %} + {{ form.as_p }} + +
+
+{% endblock %} diff --git a/core/templates/core/property_detail.html b/core/templates/core/property_detail.html new file mode 100644 index 0000000..cd13baa --- /dev/null +++ b/core/templates/core/property_detail.html @@ -0,0 +1,28 @@ +{% extends "base.html" %} +{% load static %} + +{% block content %} +
+
+
+
+ {{ property.title }} +
+

{{ property.title }}

+

{{ property.location }}

+

${{ property.price_per_night }} / night

+

{{ property.description }}

+
+
+
+
+
+
+
Book this property
+ Book Now +
+
+
+
+
+{% endblock %} diff --git a/core/templates/core/signup.html b/core/templates/core/signup.html new file mode 100644 index 0000000..dc87e80 --- /dev/null +++ b/core/templates/core/signup.html @@ -0,0 +1,22 @@ +{% extends 'base.html' %} + +{% block content %} +
+
+
+
+
+

Sign Up

+
+
+
+ {% csrf_token %} + {{ form.as_p }} + +
+
+
+
+
+
+{% endblock %} diff --git a/core/urls.py b/core/urls.py index 8e0d0ae..1a1a32e 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,7 +1,17 @@ from django.urls import path -from .views import index +from .views import index, signup_view, login_view, logout_view, property_detail, property_create, booking_create, profile, about, contact, faq urlpatterns = [ path("", index, name="index"), + path("property//", property_detail, name="property_detail"), + path("signup/", signup_view, name="signup"), + path("login/", login_view, name="login"), + path("logout/", logout_view, name="logout"), + path("property/create/", property_create, name="property_create"), + path("property//book/", booking_create, name="booking_create"), + path("profile/", profile, name="profile"), + path("about/", about, name="about"), + path("contact/", contact, name="contact"), + path("faq/", faq, name="faq"), ] diff --git a/core/views.py b/core/views.py index fff1779..3313453 100644 --- a/core/views.py +++ b/core/views.py @@ -1,5 +1,9 @@ -from django.shortcuts import render -from .models import Property +from django.shortcuts import render, redirect, get_object_or_404 +from django.contrib.auth import login, authenticate, logout +from django.contrib.auth.forms import UserCreationForm, AuthenticationForm +from django.contrib.auth.decorators import login_required +from .forms import PropertyForm, BookingForm +from .models import Property, Booking def index(request): properties = Property.objects.all()[:6] @@ -7,3 +11,76 @@ def index(request): 'properties': properties } return render(request, 'core/index.html', context) + +def property_detail(request, property_id): + property = get_object_or_404(Property, pk=property_id) + return render(request, 'core/property_detail.html', {'property': property}) + +def signup_view(request): + if request.method == 'POST': + form = UserCreationForm(request.POST) + if form.is_valid(): + user = form.save() + login(request, user) + return redirect('index') + else: + form = UserCreationForm() + return render(request, 'core/signup.html', {'form': form}) + +def login_view(request): + if request.method == 'POST': + form = AuthenticationForm(data=request.POST) + if form.is_valid(): + user = form.get_user() + login(request, user) + return redirect('index') + else: + form = AuthenticationForm() + return render(request, 'core/login.html', {'form': form}) + +def logout_view(request): + if request.method == 'POST': + logout(request) + return redirect('index') + +@login_required +def property_create(request): + if request.method == 'POST': + form = PropertyForm(request.POST, request.FILES) + if form.is_valid(): + property = form.save(commit=False) + property.owner = request.user + property.save() + return redirect('property_detail', property_id=property.id) + else: + form = PropertyForm() + return render(request, 'core/property_create.html', {'form': form}) + +@login_required +def booking_create(request, property_id): + property = get_object_or_404(Property, pk=property_id) + if request.method == 'POST': + form = BookingForm(request.POST) + if form.is_valid(): + booking = form.save(commit=False) + booking.property = property + booking.user = request.user + booking.save() + return redirect('profile') + else: + form = BookingForm() + return render(request, 'core/booking_create.html', {'form': form, 'property': property}) + +@login_required +def profile(request): + bookings = Booking.objects.filter(user=request.user) + return render(request, 'core/profile.html', {'bookings': bookings}) + +def about(request): + return render(request, 'core/about.html') + +def contact(request): + return render(request, 'core/contact.html') + +def faq(request): + return render(request, 'core/faq.html')