From a021ca24bf7982ea8573199751ef98af773114c4 Mon Sep 17 00:00:00 2001 From: Vitalii Safian Date: Wed, 22 Nov 2023 22:48:49 +0200 Subject: [PATCH 1/7] solution --- taxi/migrations/0001_initial.py | 179 ++++++++++++++---- ...002_alter_manufacturer_options_and_more.py | 11 +- .../0003_alter_manufacturer_name.py | 7 +- taxi/models.py | 6 +- taxi/urls.py | 6 +- taxi/views.py | 20 +- taxi_service/settings.py | 15 +- taxi_service/urls.py | 1 + templates/includes/sidebar.html | 10 + templates/registration/logged_out.html | 5 + templates/registration/login.html | 14 ++ templates/taxi/driver_list.html | 3 + templates/taxi/index.html | 1 + 13 files changed, 213 insertions(+), 65 deletions(-) create mode 100644 templates/registration/logged_out.html create mode 100644 templates/registration/login.html diff --git a/taxi/migrations/0001_initial.py b/taxi/migrations/0001_initial.py index ff29293e..9756f527 100644 --- a/taxi/migrations/0001_initial.py +++ b/taxi/migrations/0001_initial.py @@ -1,63 +1,178 @@ # Generated by Django 4.0.2 on 2022-03-24 06:38 -from django.conf import settings import django.contrib.auth.models import django.contrib.auth.validators -from django.db import migrations, models -import django.db.models.deletion import django.utils.timezone +from django.conf import settings +from django.db import migrations, models 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)), - ('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)), + ( + "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)), - ('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)), + ("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/migrations/0002_alter_manufacturer_options_and_more.py b/taxi/migrations/0002_alter_manufacturer_options_and_more.py index a056822c..178da235 100644 --- a/taxi/migrations/0002_alter_manufacturer_options_and_more.py +++ b/taxi/migrations/0002_alter_manufacturer_options_and_more.py @@ -4,19 +4,18 @@ class Migration(migrations.Migration): - dependencies = [ - ('taxi', '0001_initial'), + ("taxi", "0001_initial"), ] operations = [ migrations.AlterModelOptions( - name='manufacturer', - options={'ordering': ['name']}, + name="manufacturer", + options={"ordering": ["name"]}, ), migrations.AlterField( - model_name='driver', - name='license_number', + model_name="driver", + name="license_number", field=models.CharField(max_length=255, unique=True), ), ] diff --git a/taxi/migrations/0003_alter_manufacturer_name.py b/taxi/migrations/0003_alter_manufacturer_name.py index 806cc0e6..7bf3309d 100644 --- a/taxi/migrations/0003_alter_manufacturer_name.py +++ b/taxi/migrations/0003_alter_manufacturer_name.py @@ -4,15 +4,14 @@ class Migration(migrations.Migration): - dependencies = [ - ('taxi', '0002_alter_manufacturer_options_and_more'), + ("taxi", "0002_alter_manufacturer_options_and_more"), ] operations = [ migrations.AlterField( - model_name='manufacturer', - name='name', + model_name="manufacturer", + name="name", field=models.CharField(max_length=255, unique=True), ), ] diff --git a/taxi/models.py b/taxi/models.py index 9061de82..f6197437 100644 --- a/taxi/models.py +++ b/taxi/models.py @@ -9,7 +9,7 @@ class Manufacturer(models.Model): class Meta: ordering = ["name"] - def __str__(self): + def __str__(self) -> str: return f"{self.name} {self.country}" @@ -20,7 +20,7 @@ class Meta: verbose_name = "driver" verbose_name_plural = "drivers" - def __str__(self): + def __str__(self) -> str: return f"{self.username} ({self.first_name} {self.last_name})" @@ -29,5 +29,5 @@ class Car(models.Model): manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE) drivers = models.ManyToManyField(Driver, related_name="cars") - def __str__(self): + def __str__(self) -> model: return self.model diff --git a/taxi/urls.py b/taxi/urls.py index c663d6e2..25d25e4c 100644 --- a/taxi/urls.py +++ b/taxi/urls.py @@ -10,7 +10,7 @@ ) urlpatterns = [ - path("", index, name="index"), + path("taxi/", index, name="index"), path( "manufacturers/", ManufacturerListView.as_view(), @@ -19,9 +19,7 @@ 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" - ), + path("drivers//", DriverDetailView.as_view(), name="driver-detail"), ] app_name = "taxi" diff --git a/taxi/views.py b/taxi/views.py index 82ad312f..4158f60b 100644 --- a/taxi/views.py +++ b/taxi/views.py @@ -1,47 +1,53 @@ +from django.contrib.auth.decorators import login_required +from django.contrib.auth.mixins import LoginRequiredMixin +from django.http import HttpRequest, HttpResponse from django.shortcuts import render from django.views import generic from .models import Driver, Car, Manufacturer -def index(request): +@login_required +def index(request: HttpRequest) -> HttpResponse: """View function for the home page of the site.""" - num_drivers = Driver.objects.count() num_cars = Car.objects.count() num_manufacturers = Manufacturer.objects.count() + num_visits = request.session.get("num_visits", 0) + request.session["num_visits"] = num_visits + 1 context = { "num_drivers": num_drivers, "num_cars": num_cars, "num_manufacturers": num_manufacturers, + "num_visits": num_visits + 1, } return render(request, "taxi/index.html", context=context) -class ManufacturerListView(generic.ListView): +class ManufacturerListView(LoginRequiredMixin, generic.ListView): model = Manufacturer context_object_name = "manufacturer_list" template_name = "taxi/manufacturer_list.html" paginate_by = 5 -class CarListView(generic.ListView): +class CarListView(LoginRequiredMixin, generic.ListView): model = Car paginate_by = 5 queryset = Car.objects.select_related("manufacturer") -class CarDetailView(generic.DetailView): +class CarDetailView(LoginRequiredMixin, generic.DetailView): model = Car -class DriverListView(generic.ListView): +class DriverListView(LoginRequiredMixin, generic.ListView): model = Driver paginate_by = 5 -class DriverDetailView(generic.DetailView): +class DriverDetailView(LoginRequiredMixin, generic.DetailView): model = Driver queryset = Driver.objects.prefetch_related("cars__manufacturer") diff --git a/taxi_service/settings.py b/taxi_service/settings.py index b6c0cf19..5a96de0d 100644 --- a/taxi_service/settings.py +++ b/taxi_service/settings.py @@ -20,9 +20,7 @@ # See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = ( - "django-insecure-8ovil3xu6=eaoqd#-#&ricv159p0pypoh5_lgm*)-dfcjqe=yc" -) +SECRET_KEY = "django-insecure-8ovil3xu6=eaoqd#-#&ricv159p0pypoh5_lgm*)-dfcjqe=yc" # SECURITY WARNING: don"t run with debug turned on in production! DEBUG = True @@ -93,16 +91,13 @@ "UserAttributeSimilarityValidator", }, { - "NAME": "django.contrib.auth.password_validation." - "MinimumLengthValidator", + "NAME": "django.contrib.auth.password_validation." "MinimumLengthValidator", }, { - "NAME": "django.contrib.auth.password_validation." - "CommonPasswordValidator", + "NAME": "django.contrib.auth.password_validation." "CommonPasswordValidator", }, { - "NAME": "django.contrib.auth.password_validation." - "NumericPasswordValidator", + "NAME": "django.contrib.auth.password_validation." "NumericPasswordValidator", }, ] @@ -133,3 +128,5 @@ # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + +LOGIN_REDIRECT_URL = "/taxi/" diff --git a/taxi_service/urls.py b/taxi_service/urls.py index 8b94449f..baf83d1a 100644 --- a/taxi_service/urls.py +++ b/taxi_service/urls.py @@ -22,4 +22,5 @@ urlpatterns = [ path("admin/", admin.site.urls), path("", include("taxi.urls", namespace="taxi")), + path("accounts/", include("django.contrib.auth.urls")), ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/templates/includes/sidebar.html b/templates/includes/sidebar.html index b7cd72dc..a7a4c6e7 100644 --- a/templates/includes/sidebar.html +++ b/templates/includes/sidebar.html @@ -1,4 +1,14 @@ diff --git a/templates/taxi/index.html b/templates/taxi/index.html index 13c59aa8..4d30ced2 100644 --- a/templates/taxi/index.html +++ b/templates/taxi/index.html @@ -10,4 +10,5 @@

Dynamic content

  • Drivers: {{ num_drivers }}
  • Manufacturers: {{ num_manufacturers }}
  • +

    You have visited this page {{ num_visits }} time{{ num_visits|pluralize }}.

    {% endblock %} From 8f2f3e2edcc7d5f10a43ca802bc000563541fba6 Mon Sep 17 00:00:00 2001 From: Vitalii Safian Date: Wed, 22 Nov 2023 22:55:45 +0200 Subject: [PATCH 2/7] solution update --- db.sqlite3 | Bin 0 -> 176128 bytes templates/registration/login.html | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 db.sqlite3 diff --git a/db.sqlite3 b/db.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..ce90c3b61b586ecf5a494407ccd1e5fb6b33bc34 GIT binary patch literal 176128 zcmeI53veUJd6+T400R&Nn#;%H_ISJ?={B2EdRU z5)Up0*d4l$0E)WXqM56uFdg5+{*Wshk}pl3c3N#fd5^ zDRxwrT(+dD{M|Fa3|=H}ty6CAe%@`*bobx?{QdRcGt=EOgSG36N<|hoOXY%85hIp9 z3&&Yr5=D!}GNJz)fGr3AX5b$W|LT7SJuVljm1Ur+Umkma`m=& zHFaA|Zmh2?EQ6+{)bjdy(O;AbvR}L>m9yJY`CK>_3pC0>Znjh{R>}um1OgAGrvzUr z#XU+HRmhcQZVo zwLF(v(}8NT`e6d%Jk4Kaz!#Pm))$hCi?{W})I0z(YMAbb-mG7$R<=z@j(3c}!-P-p zg+kn;kv7I#e6m#;vdN&WJhcvW8 ziN(`qmkgOA&R1>?3BIv0?tS-Lk(;E1u8<##?w+Ko_e*7P(RyDDg=d=jVqQ`!>3j+N z*DvOzid<0&GV9;qhKib2tKf^Y*MXClO8JJ1LoQ8i?&qOc(9kVzEH7N&NP*y{Qifw3 zA__9hjKf9bf~4e)iiBfP<6ye66=`#`-B73ooc!SY(Vj(XoZf+!lL3)Yef<mjOUET@tCM&P21Z zY$7Z(ifzK)4i4KU)J3DIt=o!FAcjr!QWU67c%g@Z@unWuRxYijfZdr;IGD|BCOX^g zIhNgR%8oL*UI47!@i=`oTY`Hyb{Ok=ffhu;Y%UUxWr9X`(*zA9YH#;m*CuOFF%;0Y zZ92@c>jqBNqz3xma*yCkB)CV@M*nLwqH{yII`qc2qL0-nH7&=x3uLm#?u5;?KfJTI zoP3UVj6m&*L-3U%T3n)Rp20hIXiI8 z>Y$gI+pH$~{i`b9t;)r$+!D1l)u1Kw96d>&hmwKXZB83>N@h9*WveVz=rB#MxshOP z((7-_e_SqIZqN?Dpg=Al%`4`6Kp07%JOjay&qKqWU$T(jCx4qP607%%-uHN?J%8@` zq$lk;+52C5f3Y{!JLrDh{W15t`*hE1Ac}uT00|%gB!C2v01`j~NZ_YO;Kd=kWhj-T zlg7bdFp`$?6<8E8ukoTa3=+& zJ$B!omWnwTn3d&@%EHljEE*ZGTSmr{s;n)@fJLFSQdBC6lJyAoGM9Ql(OO;6j9F2A zf#E1UP^;<4RTt=acU{l4uIHYVSAb8c3`+puS#r5tg02F-Ds6Res!-xWVj4UmoOFV+ zg}SnMx(uUZr7YJcJL&Ydng+)Wx3v>hLF*Ns6+p$+x{8>&UdECOEGy*Oy9w111|5l) z9H67#*1@P*M}rOY)b_Rp4KoSwmc*PLG}PO=ajsy^#FkSILray!K}q-u4@&CJ8>~C8 zY5M3Wuc4a4w6jjxKoj&d=DdfTD4@XfOo$%ZYPXDEPUqEmZJ zgQ9qBngdPat(v-bT`(a|8zJZ+cP%b5WhGxA|C9VJ@|)x%YF zB9hlEf~z9+I&C8jMSa9!8`EN%O80=>HtE!#8({N@bmGs$+eV#CADhk^;^3%l&lU^; z(E*p9tj{3$thPv9(mbi)EUG;q?j)NQ@_)#e$=AuJ$*+=4^7n}PgtZHriv*AW56%pqC#SVB>q3HE<5`L*p!Bo-7auKXi(vYBK9Ig@Nu!?7E5^6!*j!&lRo1Q*&{xlo8kmk+&qMQ<`mCGI6>EA&KcHnbcN=e zr%&?SBqvbK=Hyc+cy5eyutR9$(WeG@?kwlfm1}7}ElpRerJZE9JE%e}VW^+y#yLAv zr4M5V2YD{Q*>zod;((T@E7KGEnH_ejO;0(&4CXa<+V^_sbMt*q@mz@G>kN$yqGi-s z8X4Xpo}1u!#?(mlF!OCpJsU>%GV^V^VwU9Al5~|UsfX~~Y0gHKvAByVw=y-x@Y~56 z&Z_G&(giJDS7@Ze8~|{#Qmsa!y`OiT<(TIB7@i;GT@#$Hww`EXiMq~uqP35Ajl%>$ zuJIlbegEG{WDEHo`5O68Z1UW4otJ-p@A@TIZ@KP<2czra(?S9)C_F|bgP z@&}jZlKn93A0F1#&r3=nFPDd4^gleRr`;&!ch&pwvBu&64F7$j+M$$+TFofxwnAVI zz;}uzu2mmYcNGHTe?4KnbWo~DLonta2yCn+FUv)_Eak%yIOaTlGL)M~F%$D*+`ux9_tXat4Bx~e@ zu6Z87cG4E&+UEeQJ+@(&KK`fc|CYWti3F-e0!RP}AOR$R1dsp{Kmter2_OL^fCSzW z1lafgNXkNfO#Xs=AHD+cZSpPh4R{yeKa>ARevf>S{7dp#@+tC9$;aTA0e+eMeRw;d zM&3iJq(l^wCGUnW1zsmtK>_|D0VIF~kN^@u0!RP}AOR$R1dsp{c)Js@+c?fCvhXwu zpJm|)4TWb|ILyMQS$K+uj;C1YW8p~_4$;s)$U^u5K-lkR;Rzb@eJms_^s>-HLt8Hk z-7M^3p^JuACkq7@I#_7uZ7zHVDd!``xY*?Ylj^StKy$DWF3)$^Rk+WXDk-|hXC z-g57?-ZSp+yT9aq)xGb&<(>fr_=g0L01`j~NB{{S0VIF~o^S$Bi*^e)GVZ*m$oJD( zsXQkYMY=D-{qNUQ_AJ)g<+TG zoPP##rltjURQO@?>ZX*fRLin)cynTK7^EZ53%VWVENZT9kU2T{v{pj5%$!9_=q8$z zpF0I5CSwA%)tpYnsnO;%@hOlUn{Y7eb7ke8$!;xr#s?B-ryRP)hKz2jE_G6q(ycV4 zbo;b~XNN#)G~l3?86wmsE#@DD{P9UUvqqIwRVfu4t*FP}H2^Y!kX^UOB&8cvm+RN$ zbc;-Kx=DJ9cmm|cChXKElMppZk52S~#N;&3tjd;(6}ecc9PG&r%k(snCQcC$4n5E7 z=Cz3HCN>rDY6WyVTMFp5nhOLxP+%g)Q-fQisnKT9XL~_(G+<+vZ z_@qrYwjrULsmb(cGP->Y8Qn4#f5rter_b7`NeuyN42zs|LheMw%B`P-$Nx`atD((E00|%gB!C2v01`j~NB{{S0VIF~kN^@u0!RP}AOR$R1dsp{ zKmter2_OL^fCQQe(Dna5?`szFZSqItOXQQ}7s=lto8&rqk%Y)FA>P06eiyz6_*!!* zj3EIefCP{L5)^HNq!saYv6HyoL|ZLMFqaXxdpDUys|DXZ!9i~*H#ynlB>7HtEt;!a$|jEVHvDl zN-eLS7yU)4Ap6C8QW?IuSUwky#R83Tkee-4={Fv`2m~HVPYJ$MihGnYFlk>aO;=U9 zZ2Fd@%7}Ey>+b-si|Aw3QP)M;WRxwy)2O1jn!1!)O)bx**7QcxWc9-Y#Ce*(${KZH zd0~Aaxwv>+PfX1NAmiJZ>5k~l`lV`R+l1tJ#|S)3_yk`l#622mW6ZuF+C)}MInkY` zCh%6H$gDahKqh-kb{31Ze_@QXD8-z-uinio@Ofisg%XRW%`O=-Mc@p;eZx04#=Y-; zD{_;R&=vAy(cP0&^*)?Djjk37&ouSLyrfp>$4`}_U(87rxuO(g*1y3G6*a9^!53+- z11B$)@(mYMqC497S`6l9nghl|JsNy!@(3CE(w!E|FQ z(&lEnp->I@Ks0=4xjl>4IK2ZcCj%m*`uZu3+NEUWqAI70)k20s=y*yCLq zg7r|%8AHZ?U+u(z;2Ry~Y6a#GMvk%PZuSNvd_TxY`nPW_Cc+ z$w>dwahiU189G)vycwO2%h61WT#s({!t#9TmgsNEt$#wfO^aV#S#Fc2HElVpKTumc zA@~j^xQFCeW@bw{nN~g>3`v>DbWBQDi^|;=v+TOjn%j!Uw7s+HW7h;ctggWycsSlC z_$DT}N1Wjh2K*LtNe9<3gdfX4S~0g@^3B(s+zx!Vz0}m0`caR*zBTIH=ue@b>C^#K zntiqD1|m?qNCe+jf~!eJ>ucT10HB&KiPTePqS;tB5tbRnHeqiEhiwz;qS4gWZAB;$ z!zOwu3e+aN(8IuZQx9t^r+w0!+MNl7gW1exqO;weW7(~#Hz_;Hjhd61+%$GIF<<--L0*rz1@3Vo2)^liD|m&FvqSNI9Zb#=zq&Sf-jNa9!(ql zug!?g4dLq08{3LLR;Sdo9PciW$sW5CHrM{}&faqJIo>e>wJQ$6SBh}8_b}(_aK=bW zdwc1;l#%o40-U7t>FIbT7s*93%w;+h;5+(D2f@QNYhKc!7_C{uCO`NHGYdMxf?e=M zBHY6S^NkKAJ0v-py`qD}W4VN3U)R&8W_9Q6z%{FbUS@8yn&|hhs(iO9!$7(vYHO-N zOXfLxl0FY51GU?nHn5exb z81T4;Exi`S;{B-eyyHiX3Ht~5@7n&vHf+7beZlgVkfHQ`zW1y<;C{X5)t-eOXAc|d zH%8;f->G@d2)?CBt~SYf3mX)`ubt>W+o94QL7`WKXf7NIN^x!Wz?3iQNc9_+ql#-i zpjC=;*Ny`&|Kgb7+XZ`IdIttNt)g{%8pGgpMv7%KGw}rLyRG7K?~r4@(P~3yY0?H>I{d8Q+X!*B7uqnedkfXL1}yB^ zzNKfNlZUv6wxbQKS1X+hZEi|4nOK|KXy!h3q_b#pCKu_f`gnB!pQsmyZgF8)@J&u~ zj|jW0*H4f2D^hFRr<-b?tu|etXk&J~k3DQ6S-%(^J;Mru`T>p}ei}`7v|dg2c&Y=n z{L_MOWQ2QVq6t_X)L>^oj|HaX4zFp142{@&0Xn*(<`T!OuEk=ny<^rIUJN;WYm?pP z_=@lYcuiBk;ntMrUJSk=1))|20lF})ty0vVxap_QV{9P7{ z?OUu-`X=CB6YiF^dre(R$`zC%onF^guIRmHEFP6JQoL1QtYf2eFW=N84Kd1&-A}Tq zrgP5;zBHJ0-Rvg~*`}#Gb{ibcWaSw-(`uUk2-8~T4bxd&Pj?erO85h>yf?tEH1CHM z6IdB-wV*XVu3MyEX0U zlh?`D$!p}x4Qyi2O47AbEwnhp1#11o00EAOR$R1dsp{Kmter2_OL^ zfCP{L66i$0G00muu8(&N&@FK}`svo&<2XUL9>D=GEWp<7fR`0GuGis!*AzI;EjZvE z1=!jg@J<59^*9~yJ_5(NTn=~xf#U?b1KvI0I0x^5Hx4+??zY2A2Aqp`z&iyTXR|xt zRRY*r?eOXV=dwBAMFEbp+8pq50Bo&xcq72Y*?OqOeVxq4G$eookN^@u0!RP}AOR$R z1dsp{KmthM?Lq*@|8JL;#CjkBB!C2v01`j~NB{{S0VIF~kN^_sP5{UM-PvFc5!qR7!@^z~y4@`7p`pjc zLMIJv0u8MW8anJOTk@}A2H-cz zKOr9_A0od%evUjM6_N)*{6hjr00|%gB!C2v01`j~NB{{S0VIF~Iua0gj^q0HLAE8X z0k-w_^s}u;IH7IbeQew7AZ+UvyxP|0VcQ;OFWb6YZnhQdJ#6dXU2JQ2I~_dd;&_2Y zZFYyYwc1&-jb~99^J`nH)y}&(mrWc0e}Jz4*Vq2(a{zSBpRWGX=KwwpPXc@#<^euT z{sH+w#|mO15|6AGmztyz<|3M3^ z{eO-80G|B+5&2W{JLD_mgYZ7U?~{K{K0|(;e1ZHM@=N3+@MOSm9J4Ygh6IoR5>^n0C2Dg0D(;a*i0_~XcGW- zHUYr12>`c~x4Aer{k{0vrC@^x;2m)7SqkeVq-$R3v}|kN^@u0!RP}AOR$R z1dsp{KmthM?LmM(|BvJUw?|uIEsy{bKmter2_OL^fCP{L5;DHRz`(+FK zKL)%0ZgQB81dsp{Kmter2_OL^fCP{L5=|QCsy&%85e`9A!UD-NFUSHjuyp-Emx_o7IHk+#??ky~=EN!nx z@2BK&A)ZOxO6_k}5__=+4?>mb&ArXt(9B)fzY@G&Dy~oO-zinET)T5Uv%gy1h#eG` zN(Zrn*~sc@Jb0k08YUyAnd2@eXiDg3Z8aThevN^Z#Vd;uc=D2(NlS?D?)|y7%YYUvWRx zv+4S*^T*B`2oVV&0VIF~kN^_+*$}8DIj^PmLee$r8;ZDGk&(U3Zf-N2R=1^aEPf`E z2*Tf)S@}+7CAGNrVCDLao9fFCuFu{|?gh`pm*R!sUNUthxqLZ#XSGxg=a%F<;n3xI z^|BP(y}q{?J%~#;^0C#W7qpv#&}2BQzZ4h>T}VX2u}DnH6_nzIsw$V0bZ-N8jHH(; z(!O#bTPi?#c%yIz{ue`$i?NxD;Y4zNDHc!6L~CPisKfIX=jj1o$k|?pFsno2U?X^K zaee-3elZxnCg(OPyWxEJ%;MFZotc%;{$=Ib<<#!{-i~@@cV;@52`P(Pm6_bu{N2>t zgRH9DQ%adq=0Z`vpE;Ny(#^KYh= zNJSsJ^dM>0)WhZ?=G7)gYw(JtNSQ^Rkj(FLho0Vl|msnajMF>^z2a#a z(w3rzmEEg1?rgq1KbyV2bZ_PA?H6{G!iDY9-kw~3St(p$J4Vu>mM$!X<1w)GIjd&r zn9mpMYUy<3PUv=QI+HD_**lSGHBn9_?gh_8*HYJm>y=ygU%vWaB{ns;n!B`CURO4y zigImd_fBMHYbktX=IUKFxAnqK=^!iD;li#?wvpJ)*y)SG=*ug&!|`Zr25t+!2v{RO ziwkz+f(uO>y%>xbm*vO2Qq%Q+H%VH^_sI%Ll24I8Bwr%0llPMh`887g+1&BqQIG%< zKmter2_OL^fCP{L57+u< z^L;{}ZJaaDs5u60lU(aum7NxBo(bZ;wo%TQZm|*D7)Uf`G^`%msLPl((8m8|3;A>M zUQ&j!|JTV^$hXNS$S;uJCcpZYr;i3A0VIF~kN^@u0!RP}AOR$R1dsp{c#9I~wT*D~ zC(zt1qCeTyLnHMkrd%{qe;CP0BlU-T1SY0GLgS#3`a>yp8mT|*!Lx||JcUim(VkbZ zx@{w_`jdJd;RlvpVZuVTJaX^nd(XOy?oiLKIscWj=6qS0a0+ixh-elPKmter2_S(t zjev93@--g>f8kHM!4Qn9)zWh>RPTy9Gc=2lb5^^~}t zoLx+b{MqAz%SmD%2{}({9HH|3p9#?Kv62lU2?^Z<%R1TsQ?h`PbWBw0uRLQt9SEC zB^hz}Hm1xea$2fZN=zc%W_dc)mgJ0u9KMCILsjI;%00Q94zt2qsNmcKc%FtqU_Mz}h6Z^cRR?_*>mQwVKIjJI7 zl!DBf6xu~m(`pqSiKVYq_{B`AlsC5;O>J(uP%LQZ)>{tLY$|0q#v!60!^}8bL@r25 z-l#}87Bvp08(Wb!H`@(`YJk5(JGN)h8mD)_XJkNRRA2XMYL}9gi>jP1Rtp&lp^mZ1 zh3igQ8@wR+)&g8D!JII+BNewwX;oHLrBqDI`+G`RPP0Qwv1~jXk4KtC&gzZ7usol- zCHk8)JC^s0E6c5d)KN^w@&}%ubof>R-Q2w8D52)~yK3?Cf-ex@9-d`h+nn1RJJamD z&6(7yI*DD`bTWvVea3Ve0r+)!KqmRcio9Pj`=7av?QIvR^-T!Al}WA^GB*ZWMx_3p zrtX_oa_MX=9+fjvTsOBNFxIg#8rf~-o0_5_MystK$seeldrt7B!KCY*P0E(wv|6mB zD+hZrGcB6Q$}@7N)inPRrghWP-Ncp>{=h5m4G6x;N$&k{3WF<0s|Bs`aor;Qy3v~b zSm&-%W=GCefJ43M;W`6Op(mH+J#%*qn!E^vXn%(;x0#o={GwWr@_D#oiPfU2Y(ZCV zc#$}FB{g>yny5+sZ@eG|1E2)1RmCkiU4Z^8ZNbT-)d5?f8COMmMO;l?O0A}r=Td8p z4r(r-BMFG}RBn}BZx@yq))$hCi?{W})I3;G_Z~5$z)9NZ0Zj)mT|thmSg$iZ;deOq z1!^b9;baERs-4W(5wcP_tzC^&eM{XmG&3W`HWP_fkLogin {% if form.errors %} -

    Invalid credentials

    +

    Invalid credentials!

    {% endif %}
    {% csrf_token %} From 7b3bf40087cc61c118640f0baebcabcca99421bb Mon Sep 17 00:00:00 2001 From: Vitalii Safian Date: Wed, 22 Nov 2023 22:57:50 +0200 Subject: [PATCH 3/7] - --- taxi/urls.py | 3 ++- taxi_service/settings.py | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/taxi/urls.py b/taxi/urls.py index 25d25e4c..44c3b38b 100644 --- a/taxi/urls.py +++ b/taxi/urls.py @@ -19,7 +19,8 @@ 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"), + path("drivers//", + DriverDetailView.as_view(), name="driver-detail"), ] app_name = "taxi" diff --git a/taxi_service/settings.py b/taxi_service/settings.py index 5a96de0d..b6c7296f 100644 --- a/taxi_service/settings.py +++ b/taxi_service/settings.py @@ -91,13 +91,16 @@ "UserAttributeSimilarityValidator", }, { - "NAME": "django.contrib.auth.password_validation." "MinimumLengthValidator", + "NAME": "django.contrib.auth.password_validation." + "MinimumLengthValidator", }, { - "NAME": "django.contrib.auth.password_validation." "CommonPasswordValidator", + "NAME": "django.contrib.auth.password_validation." + "CommonPasswordValidator", }, { - "NAME": "django.contrib.auth.password_validation." "NumericPasswordValidator", + "NAME": "django.contrib.auth.password_validation." + "NumericPasswordValidator", }, ] From 2543720670b7455726ff87e7eccf27611c3eff6f Mon Sep 17 00:00:00 2001 From: Vitalii Safian Date: Wed, 22 Nov 2023 23:01:23 +0200 Subject: [PATCH 4/7] flake8-blacked --- taxi_service/settings.py | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/taxi_service/settings.py b/taxi_service/settings.py index b6c7296f..8df81b34 100644 --- a/taxi_service/settings.py +++ b/taxi_service/settings.py @@ -20,7 +20,8 @@ # See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "django-insecure-8ovil3xu6=eaoqd#-#&ricv159p0pypoh5_lgm*)-dfcjqe=yc" +SECRET_KEY = ("django-insecure-" + "8ovil3xu6=eaoqd#-#&ricv159p0pypoh5_lgm*)-dfcjqe=yc") # SECURITY WARNING: don"t run with debug turned on in production! DEBUG = True @@ -86,22 +87,14 @@ # https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ - { - "NAME": "django.contrib.auth.password_validation." - "UserAttributeSimilarityValidator", - }, - { - "NAME": "django.contrib.auth.password_validation." - "MinimumLengthValidator", - }, - { - "NAME": "django.contrib.auth.password_validation." - "CommonPasswordValidator", - }, - { - "NAME": "django.contrib.auth.password_validation." - "NumericPasswordValidator", - }, + {"NAME": "django.contrib.auth.password_validation." + "UserAttributeSimilarityValidator", }, + {"NAME": "django.contrib.auth.password_validation." + "MinimumLengthValidator", }, + {"NAME": "django.contrib.auth.password_validation." + "CommonPasswordValidator", }, + {"NAME": "django.contrib.auth.password_validation." + "NumericPasswordValidator", }, ] AUTH_USER_MODEL = "taxi.Driver" From 5fc1f34f0690936519ccc5d8bf6c0b1be6fee83d Mon Sep 17 00:00:00 2001 From: Vitalii Safian Date: Thu, 23 Nov 2023 13:44:28 +0200 Subject: [PATCH 5/7] fixed annotation return type in models.py --- taxi/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/taxi/models.py b/taxi/models.py index f6197437..524decd3 100644 --- a/taxi/models.py +++ b/taxi/models.py @@ -29,5 +29,5 @@ class Car(models.Model): manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE) drivers = models.ManyToManyField(Driver, related_name="cars") - def __str__(self) -> model: - return self.model + def __str__(self) -> str: + return f"{self.model}" From b21b21e8f0b1b162d6063669e44794941a10845b Mon Sep 17 00:00:00 2001 From: Vitalii Safian Date: Thu, 23 Nov 2023 14:41:36 +0200 Subject: [PATCH 6/7] models.py --- taxi/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taxi/models.py b/taxi/models.py index 524decd3..76db7e01 100644 --- a/taxi/models.py +++ b/taxi/models.py @@ -30,4 +30,4 @@ class Car(models.Model): drivers = models.ManyToManyField(Driver, related_name="cars") def __str__(self) -> str: - return f"{self.model}" + return self.model From 8f255fb1f5ce0d451b001ff7a95af65a1ea52e58 Mon Sep 17 00:00:00 2001 From: Vitalii Safian Date: Thu, 23 Nov 2023 15:33:25 +0200 Subject: [PATCH 7/7] db.sqlite3 deleted and added it in .gitignore --- .gitignore | 1 + db.sqlite3 | Bin 176128 -> 0 bytes 2 files changed, 1 insertion(+) delete mode 100644 db.sqlite3 diff --git a/.gitignore b/.gitignore index b26d6116..b9b230d8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ venv/ .pytest_cache/ **__pycache__/ +db.sqlite3 diff --git a/db.sqlite3 b/db.sqlite3 deleted file mode 100644 index ce90c3b61b586ecf5a494407ccd1e5fb6b33bc34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176128 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