From 97588810facbfdcb3772138c18aa61c2e8698c00 Mon Sep 17 00:00:00 2001 From: Yaroslav Rybalka Date: Thu, 10 Aug 2023 15:33:31 +0300 Subject: [PATCH 1/3] Solution --- taxi/migrations/0001_initial.py | 172 ++++++++++++++++++++---- taxi/models.py | 3 + taxi/urls.py | 22 ++- taxi/views.py | 32 +++++ taxi_service/settings.py | 6 +- templates/base.html | 7 +- templates/includes/pagination.html | 19 +++ templates/includes/sidebar.html | 10 +- templates/taxi/car_detail.html | 17 +++ templates/taxi/car_list.html | 16 +++ templates/taxi/driver_detail.html | 12 ++ templates/taxi/driver_list.html | 16 +++ templates/taxi/index.html | 2 +- templates/taxi/manufacturer_detail.html | 7 + templates/taxi/manufacturer_list.html | 16 +++ tests/test_taxi_view.py | 4 +- 16 files changed, 314 insertions(+), 47 deletions(-) create mode 100644 templates/includes/pagination.html create mode 100644 templates/taxi/car_detail.html create mode 100644 templates/taxi/car_list.html create mode 100644 templates/taxi/driver_detail.html create mode 100644 templates/taxi/driver_list.html create mode 100644 templates/taxi/manufacturer_detail.html create mode 100644 templates/taxi/manufacturer_list.html diff --git a/taxi/migrations/0001_initial.py b/taxi/migrations/0001_initial.py index 25ca12567..08ed223ae 100644 --- a/taxi/migrations/0001_initial.py +++ b/taxi/migrations/0001_initial.py @@ -9,55 +9,169 @@ class Migration(migrations.Migration): - initial = True dependencies = [ - ('auth', '0012_alter_user_first_name_max_length'), + ("auth", "0012_alter_user_first_name_max_length"), ] operations = [ migrations.CreateModel( - name='Driver', + name="Driver", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), - ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), - ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), - ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), - ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), - ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), - ('license_number', models.CharField(max_length=255, unique=True)), - ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), - ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("password", models.CharField(max_length=128, verbose_name="password")), + ( + "last_login", + models.DateTimeField( + blank=True, null=True, verbose_name="last login" + ), + ), + ( + "is_superuser", + models.BooleanField( + default=False, + help_text="Designates that this user has all permissions without explicitly assigning them.", + verbose_name="superuser status", + ), + ), + ( + "username", + models.CharField( + error_messages={ + "unique": "A user with that username already exists." + }, + help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", + max_length=150, + unique=True, + validators=[ + django.contrib.auth.validators.UnicodeUsernameValidator() + ], + verbose_name="username", + ), + ), + ( + "first_name", + models.CharField( + blank=True, max_length=150, verbose_name="first name" + ), + ), + ( + "last_name", + models.CharField( + blank=True, max_length=150, verbose_name="last name" + ), + ), + ( + "email", + models.EmailField( + blank=True, max_length=254, verbose_name="email address" + ), + ), + ( + "is_staff", + models.BooleanField( + default=False, + help_text="Designates whether the user can log into this admin site.", + verbose_name="staff status", + ), + ), + ( + "is_active", + models.BooleanField( + default=True, + help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", + verbose_name="active", + ), + ), + ( + "date_joined", + models.DateTimeField( + default=django.utils.timezone.now, verbose_name="date joined" + ), + ), + ("license_number", models.CharField(max_length=255, unique=True)), + ( + "groups", + models.ManyToManyField( + blank=True, + help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", + related_name="user_set", + related_query_name="user", + to="auth.Group", + verbose_name="groups", + ), + ), + ( + "user_permissions", + models.ManyToManyField( + blank=True, + help_text="Specific permissions for this user.", + related_name="user_set", + related_query_name="user", + to="auth.Permission", + verbose_name="user permissions", + ), + ), ], options={ - 'verbose_name': 'driver', - 'verbose_name_plural': 'drivers', + "verbose_name": "driver", + "verbose_name_plural": "drivers", }, managers=[ - ('objects', django.contrib.auth.models.UserManager()), + ("objects", django.contrib.auth.models.UserManager()), ], ), migrations.CreateModel( - name='Manufacturer', + name="Manufacturer", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, unique=True)), - ('country', models.CharField(max_length=255)), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255, unique=True)), + ("country", models.CharField(max_length=255)), ], ), migrations.CreateModel( - name='Car', + name="Car", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('model', models.CharField(max_length=255)), - ('drivers', models.ManyToManyField(related_name='cars', to=settings.AUTH_USER_MODEL)), - ('manufacturer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='taxi.manufacturer')), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("model", models.CharField(max_length=255)), + ( + "drivers", + models.ManyToManyField( + related_name="cars", to=settings.AUTH_USER_MODEL + ), + ), + ( + "manufacturer", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="taxi.manufacturer", + ), + ), ], ), ] diff --git a/taxi/models.py b/taxi/models.py index 2861a656f..b79df4da1 100644 --- a/taxi/models.py +++ b/taxi/models.py @@ -6,6 +6,9 @@ class Manufacturer(models.Model): name = models.CharField(max_length=255, unique=True) country = models.CharField(max_length=255) + class Meta: + ordering = ["name"] + class Driver(AbstractUser): license_number = models.CharField(max_length=255, unique=True) diff --git a/taxi/urls.py b/taxi/urls.py index d1911da6b..e76d35e02 100644 --- a/taxi/urls.py +++ b/taxi/urls.py @@ -1,9 +1,29 @@ from django.urls import path -from .views import index +from .views import ( + index, + ManufacturerListView, + CarListView, + CarDetailView, + DriverListView, + DriverDetailView, + ManufacturerDetailView, +) urlpatterns = [ path("", index, name="index"), + path("manufacturers/", ManufacturerListView.as_view(), + name="manufacturer-list"), + path( + "manufacturers/", + ManufacturerDetailView.as_view(), + name="manufacturer-detail", + ), + path("cars/", CarListView.as_view(), name="car-list"), + path("cars//", CarDetailView.as_view(), name="car-detail"), + path("drivers/", DriverListView.as_view(), name="driver-list"), + path("drivers//", DriverDetailView.as_view(), + name="driver-detail"), ] app_name = "taxi" diff --git a/taxi/views.py b/taxi/views.py index 8e16b5565..b45e28d8e 100644 --- a/taxi/views.py +++ b/taxi/views.py @@ -1,4 +1,5 @@ from django.shortcuts import render +from django.views import generic from .models import Driver, Car, Manufacturer @@ -17,3 +18,34 @@ def index(request): } return render(request, "taxi/index.html", context=context) + + +class ManufacturerListView(generic.ListView): + model = Manufacturer + context_object_name = "manufacturers_list" + queryset = Manufacturer.objects.all().order_by("name") + paginate_by = 5 + + +class CarListView(generic.ListView): + model = Car + paginate_by = 5 + queryset = Car.objects.all().select_related("manufacturer") + + +class CarDetailView(generic.DetailView): + model = Car + + +class DriverListView(generic.ListView): + model = Driver + paginate_by = 5 + + +class DriverDetailView(generic.DetailView): + model = Driver + queryset = Driver.objects.prefetch_related("cars__manufacturer") + + +class ManufacturerDetailView(generic.DetailView): + model = Manufacturer diff --git a/taxi_service/settings.py b/taxi_service/settings.py index 7b70d66c7..8a834eeee 100644 --- a/taxi_service/settings.py +++ b/taxi_service/settings.py @@ -92,15 +92,15 @@ }, { "NAME": "django.contrib.auth.password_validation." - "MinimumLengthValidator", + "MinimumLengthValidator", }, { "NAME": "django.contrib.auth.password_validation." - "CommonPasswordValidator", + "CommonPasswordValidator", }, { "NAME": "django.contrib.auth.password_validation." - "NumericPasswordValidator", + "NumericPasswordValidator", }, ] diff --git a/templates/base.html b/templates/base.html index 272284d17..ef822b334 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,6 +1,5 @@ - {% block title %}Taxi Service{% endblock %} @@ -13,7 +12,6 @@ {% load static %} -
@@ -29,11 +27,10 @@ {% block content %}{% endblock %} {% block pagination %} + {% include 'includes/pagination.html' %} {% endblock %} -
- - + \ No newline at end of file diff --git a/templates/includes/pagination.html b/templates/includes/pagination.html new file mode 100644 index 000000000..8c64a1985 --- /dev/null +++ b/templates/includes/pagination.html @@ -0,0 +1,19 @@ +{% if is_paginated %} +
    + {% if page_obj.has_previous %} +
  • + Previous +
  • + {% endif %} + +
  • + {{ page_obj.number }} of {{ paginator.num_pages }} +
  • + + {% if page_obj.has_next %} +
  • + Next +
  • + {% endif %} +
+{% endif %} \ No newline at end of file diff --git a/templates/includes/sidebar.html b/templates/includes/sidebar.html index b42a684ac..e9bf9aac0 100644 --- a/templates/includes/sidebar.html +++ b/templates/includes/sidebar.html @@ -1,6 +1,6 @@ +
  • Home page
  • +
  • Manufacturers
  • +
  • Cars
  • +
  • Drivers
  • + \ No newline at end of file diff --git a/templates/taxi/car_detail.html b/templates/taxi/car_detail.html new file mode 100644 index 000000000..e5476558f --- /dev/null +++ b/templates/taxi/car_detail.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} + +{% block content %} +

    Car detail:

    +
      +
    • Manufacturer: {{ car.manufacturer.name }}
    • +
    • Country of manufacturer: {{ car.manufacturer.country }}
    • +
    +
      + {% for driver in car.drivers.all %} +
    • Driver license number: {{ driver.license_number }}
    • +

      Driver name: {{ driver.first_name }}

      +

      Driver last name: {{ driver.last_name }}

      +

      Is active: {{ driver.is_active }}

      + {% endfor %} +
    +{% endblock %} \ No newline at end of file diff --git a/templates/taxi/car_list.html b/templates/taxi/car_list.html new file mode 100644 index 000000000..3bfad26e5 --- /dev/null +++ b/templates/taxi/car_list.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} + +{% block content %} +

    Car List

    +
      + {% if car_list %} + {% for car in car_list %} +
    • + {{ car.model }} +
    • + {% endfor %} + {% else %} +

      There are no cars

      + {% endif %} +
    +{% endblock %} \ No newline at end of file diff --git a/templates/taxi/driver_detail.html b/templates/taxi/driver_detail.html new file mode 100644 index 000000000..955b380a1 --- /dev/null +++ b/templates/taxi/driver_detail.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} + +{% block content %} +

    Driver's car with a license number {{ driver.license_number }}

    +
      + {% for car in driver.cars.all %} +
    • {{ car.model }}
    • +

      Manufacturer name: {{ car.manufacturer.name }}

      +

      Country: {{ car.manufacturer.country }}

      + {% endfor %} +
    +{% endblock %} \ No newline at end of file diff --git a/templates/taxi/driver_list.html b/templates/taxi/driver_list.html new file mode 100644 index 000000000..729e74d3e --- /dev/null +++ b/templates/taxi/driver_list.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} + +{% block content %} +

    Driver List

    +
      + {% if driver_list %} + {% for driver in driver_list %} +
    • + {{ driver.username }} +
    • + {% endfor %} + {% else %} +

      There are no drivers

      + {% endif %} +
    +{% endblock %} \ No newline at end of file diff --git a/templates/taxi/index.html b/templates/taxi/index.html index 13c59aa8a..e0f414614 100644 --- a/templates/taxi/index.html +++ b/templates/taxi/index.html @@ -10,4 +10,4 @@

    Dynamic content

  • Drivers: {{ num_drivers }}
  • Manufacturers: {{ num_manufacturers }}
  • -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/templates/taxi/manufacturer_detail.html b/templates/taxi/manufacturer_detail.html new file mode 100644 index 000000000..71707aed1 --- /dev/null +++ b/templates/taxi/manufacturer_detail.html @@ -0,0 +1,7 @@ +{% extends "base.html" %} + +{% block content %} +

    Manufacturer detail:

    +

    Manufacturer name: {{ manufacturer.name }}

    +

    Manufacturer country: {{ manufacturer.country }}

    +{% endblock %} \ No newline at end of file diff --git a/templates/taxi/manufacturer_list.html b/templates/taxi/manufacturer_list.html new file mode 100644 index 000000000..2ab4a304d --- /dev/null +++ b/templates/taxi/manufacturer_list.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} + +{% block content %} +

    Manufacturer List

    +
      + {% if manufacturers_list %} + {% for manufacturer in manufacturers_list %} +
    • + {{ manufacturer.name }} +
    • + {% endfor %} + {% else %} +

      There are no manufacturers

      + {% endif %} +
    +{% endblock %} \ No newline at end of file diff --git a/tests/test_taxi_view.py b/tests/test_taxi_view.py index d256447e4..ec2dc6744 100644 --- a/tests/test_taxi_view.py +++ b/tests/test_taxi_view.py @@ -23,9 +23,7 @@ def test_manufacturer_list_response_with_correct_template(self): def test_manufacturer_list_paginated_correctly(self): response = self.client.get(MANUFACTURER_LIST_URL) - self.assertEqual( - len(response.context["manufacturer_list"]), PAGINATION - ) + self.assertEqual(len(response.context["manufacturer_list"]), PAGINATION) def test_manufacturer_list_ordered_by_name(self): response = self.client.get(MANUFACTURER_LIST_URL) From a4b041768929c2e0a60a9f006778924f03bffcf7 Mon Sep 17 00:00:00 2001 From: Yaroslav Rybalka Date: Thu, 10 Aug 2023 15:55:09 +0300 Subject: [PATCH 2/3] Solution --- taxi/views.py | 2 +- templates/taxi/manufacturer_list.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/taxi/views.py b/taxi/views.py index b45e28d8e..314a18ffc 100644 --- a/taxi/views.py +++ b/taxi/views.py @@ -22,7 +22,7 @@ def index(request): class ManufacturerListView(generic.ListView): model = Manufacturer - context_object_name = "manufacturers_list" + context_object_name = "manufacturer_list" queryset = Manufacturer.objects.all().order_by("name") paginate_by = 5 diff --git a/templates/taxi/manufacturer_list.html b/templates/taxi/manufacturer_list.html index 2ab4a304d..82c72409d 100644 --- a/templates/taxi/manufacturer_list.html +++ b/templates/taxi/manufacturer_list.html @@ -3,8 +3,8 @@ {% block content %}

    Manufacturer List

      - {% if manufacturers_list %} - {% for manufacturer in manufacturers_list %} + {% if manufacturer_list %} + {% for manufacturer in manufacturer_list %}
    • {{ manufacturer.name }}
    • From 9c08b5e7725df0d7b4883ab50c713bf38616d4ab Mon Sep 17 00:00:00 2001 From: Yaroslav Rybalka Date: Mon, 21 Aug 2023 16:35:22 +0300 Subject: [PATCH 3/3] Solution --- taxi/views.py | 4 ++-- templates/base.html | 2 +- templates/includes/pagination.html | 2 +- templates/includes/sidebar.html | 2 +- templates/taxi/car_detail.html | 2 +- templates/taxi/car_list.html | 2 +- templates/taxi/driver_detail.html | 2 +- templates/taxi/driver_list.html | 2 +- templates/taxi/index.html | 2 +- templates/taxi/manufacturer_detail.html | 2 +- templates/taxi/manufacturer_list.html | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/taxi/views.py b/taxi/views.py index 314a18ffc..0af4bb8e3 100644 --- a/taxi/views.py +++ b/taxi/views.py @@ -23,14 +23,14 @@ def index(request): class ManufacturerListView(generic.ListView): model = Manufacturer context_object_name = "manufacturer_list" - queryset = Manufacturer.objects.all().order_by("name") + queryset = Manufacturer.objects.order_by("name") paginate_by = 5 class CarListView(generic.ListView): model = Car paginate_by = 5 - queryset = Car.objects.all().select_related("manufacturer") + queryset = Car.objects.select_related("manufacturer") class CarDetailView(generic.DetailView): diff --git a/templates/base.html b/templates/base.html index ef822b334..9f74c0a2a 100644 --- a/templates/base.html +++ b/templates/base.html @@ -33,4 +33,4 @@ - \ No newline at end of file + diff --git a/templates/includes/pagination.html b/templates/includes/pagination.html index 8c64a1985..3b584f4c6 100644 --- a/templates/includes/pagination.html +++ b/templates/includes/pagination.html @@ -16,4 +16,4 @@ {% endif %}
    -{% endif %} \ No newline at end of file +{% endif %} diff --git a/templates/includes/sidebar.html b/templates/includes/sidebar.html index e9bf9aac0..e32a29f11 100644 --- a/templates/includes/sidebar.html +++ b/templates/includes/sidebar.html @@ -3,4 +3,4 @@
  • Manufacturers
  • Cars
  • Drivers
  • - \ No newline at end of file + diff --git a/templates/taxi/car_detail.html b/templates/taxi/car_detail.html index e5476558f..632f5cf1e 100644 --- a/templates/taxi/car_detail.html +++ b/templates/taxi/car_detail.html @@ -14,4 +14,4 @@

    Car detail:

    Is active: {{ driver.is_active }}

    {% endfor %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/taxi/car_list.html b/templates/taxi/car_list.html index 3bfad26e5..7a13d355f 100644 --- a/templates/taxi/car_list.html +++ b/templates/taxi/car_list.html @@ -13,4 +13,4 @@

    Car List

    There are no cars

    {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/taxi/driver_detail.html b/templates/taxi/driver_detail.html index 955b380a1..b55a79479 100644 --- a/templates/taxi/driver_detail.html +++ b/templates/taxi/driver_detail.html @@ -9,4 +9,4 @@

    Driver's car with a license number {{ driver.license_number }}

    Country: {{ car.manufacturer.country }}

    {% endfor %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/taxi/driver_list.html b/templates/taxi/driver_list.html index 729e74d3e..4a49663f8 100644 --- a/templates/taxi/driver_list.html +++ b/templates/taxi/driver_list.html @@ -13,4 +13,4 @@

    Driver List

    There are no drivers

    {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/taxi/index.html b/templates/taxi/index.html index e0f414614..13c59aa8a 100644 --- a/templates/taxi/index.html +++ b/templates/taxi/index.html @@ -10,4 +10,4 @@

    Dynamic content

  • Drivers: {{ num_drivers }}
  • Manufacturers: {{ num_manufacturers }}
  • -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/taxi/manufacturer_detail.html b/templates/taxi/manufacturer_detail.html index 71707aed1..8d4f5d721 100644 --- a/templates/taxi/manufacturer_detail.html +++ b/templates/taxi/manufacturer_detail.html @@ -4,4 +4,4 @@

    Manufacturer detail:

    Manufacturer name: {{ manufacturer.name }}

    Manufacturer country: {{ manufacturer.country }}

    -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/taxi/manufacturer_list.html b/templates/taxi/manufacturer_list.html index 82c72409d..380d62acf 100644 --- a/templates/taxi/manufacturer_list.html +++ b/templates/taxi/manufacturer_list.html @@ -13,4 +13,4 @@

    Manufacturer List

    There are no manufacturers

    {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %}