Skip to content

Commit a1a712b

Browse files
committed
fix available lots filter, add ordering
1 parent 1aff32e commit a1a712b

File tree

10 files changed

+140
-114
lines changed

10 files changed

+140
-114
lines changed

application/pay_parking/parking/admin.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,9 @@ def payments_link(self, parking):
5656
link = f'<a href="{url}">Ссылка</a>'
5757
return mark_safe(link)
5858

59+
@admin.display(description='Свободные места', ordering='available_lots')
60+
def available_lots(self, parking):
61+
return parking.available_lots
62+
5963

6064
admin.site.register(Parking, ParkingAdmin)

application/pay_parking/parking/models.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,29 +31,29 @@ def __str__(self):
3131
f'<br>{self.price_per_hour} руб./час'
3232

3333

34-
class Parking(AbstractParking):
35-
# available_lots = models.GeneratedField(
36-
# expression=models.F('total_lots') - models.Func(
37-
# models.Aggregate(filter=models.Q(
38-
# payments__start__lte=models.functions.Now()
39-
# ) & models.Q(
40-
# payments__end__gte=models.functions.Now()
41-
# )),
42-
# 'COUNT'
43-
# ),
44-
# db_persist=False,
45-
# output_field=models.PositiveIntegerField(),
46-
# verbose_name='Свободные места'
47-
# )
34+
class ParkingManager(models.Manager):
35+
def get_queryset(self):
36+
from paying.models import Payment
37+
38+
return super().get_queryset().annotate(
39+
available_lots=models.F('total_lots') - models.functions.Coalesce(
40+
models.Subquery(
41+
Payment.objects.filter(
42+
parking=models.OuterRef('id')
43+
).filter(
44+
start__lte=models.functions.Now()
45+
).filter(
46+
end__gte=models.functions.Now()
47+
).annotate(
48+
count=models.Count('id')
49+
).values('count')
50+
), 0
51+
)
52+
)
4853

49-
def available_lots(self):
50-
return self.total_lots - self.payments.filter(
51-
start__lte=models.functions.Now()
52-
).filter(
53-
end__gte=models.functions.Now()
54-
).count()
5554

56-
available_lots.short_description = 'Свободные места'
55+
class Parking(AbstractParking):
56+
objects = ParkingManager()
5757

5858
def __str__(self):
5959
return self.address

application/pay_parking/parking/views.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,8 @@ class UserParkingAdmin(ParkingAdmin):
2424
@admin.display(description='')
2525
def choose_link(self, parking):
2626
url = reverse("paying:create", args=[parking.id])
27-
if parking.available_lots:
28-
link = f'<a href="{url}">Выбрать</a>'
29-
return mark_safe(link)
30-
else:
31-
return None
27+
link = f'<a href="{url}">Выбрать</a>'
28+
return mark_safe(link)
3229

3330
def has_add_permission(self, request):
3431
return False

application/pay_parking/paying/admin.py

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@
55
from django.utils.safestring import mark_safe
66
from django.urls import reverse
77
from .filters import (
8-
MinCreatedAtFiler, MaxCreatedAtFiler, MinStartFiler, MaxStartFiler,
9-
MinEndFiler, MaxEndFiler, MinPriceFiler, MaxPriceFiler,
10-
MinDurationFiler, MaxDurationFiler,
11-
AddressFiler, ParkingZoneFiler,
12-
FirstNameFiler, SecondNameFiler, ThirdNameFiler,
13-
EmailFiler, IsStaffFiler, UserIdFiler, ParkingIdFiler,
8+
MinCreatedAtFilter, MaxCreatedAtFilter, MinStartFilter, MaxStartFilter,
9+
MinEndFilter, MaxEndFilter, MinPriceFilter, MaxPriceFilter,
10+
MinDurationFilter, MaxDurationFilter,
11+
AddressFilter, ParkingZoneFilter,
12+
FirstNameFilter, SecondNameFilter, ThirdNameFilter,
13+
EmailFilter, IsStaffFilter, UserIdFilter, ParkingIdFilter,
1414
MinLatitudeFilter, MaxLatitudeFilter, MaxLongitudeFilter,
1515
MinLongitudeFilter, MaxTotalLotsFilter, MinTotalLotsFilter,
16-
MaxPricePerHourFilter, MinPricePerHourFilter
16+
MaxPricePerHourFilter, MinPricePerHourFilter,
17+
MinAvailableLotsFilter, MaxAvailableLotsFilter
1718
)
1819
from pay_parking.filters import FakeFilterWithForm
1920
from django.shortcuts import get_object_or_404
@@ -44,15 +45,16 @@ class PaymentAdmin(CustomModelAdmin):
4445

4546
list_filter = (
4647
FakeFilterWithForm,
47-
MinCreatedAtFiler, MaxCreatedAtFiler, MinStartFiler, MaxStartFiler,
48-
MinEndFiler, MaxEndFiler, MinPriceFiler, MaxPriceFiler,
49-
MinDurationFiler, MaxDurationFiler,
50-
AddressFiler, ParkingZoneFiler,
51-
FirstNameFiler, SecondNameFiler, ThirdNameFiler,
52-
EmailFiler, IsStaffFiler, UserIdFiler, ParkingIdFiler,
48+
MinCreatedAtFilter, MaxCreatedAtFilter, MinStartFilter, MaxStartFilter,
49+
MinEndFilter, MaxEndFilter, MinPriceFilter, MaxPriceFilter,
50+
MinDurationFilter, MaxDurationFilter,
51+
AddressFilter, ParkingZoneFilter,
52+
FirstNameFilter, SecondNameFilter, ThirdNameFilter,
53+
EmailFilter, IsStaffFilter, UserIdFilter, ParkingIdFilter,
5354
MinLatitudeFilter, MaxLatitudeFilter, MaxLongitudeFilter,
5455
MinLongitudeFilter, MaxTotalLotsFilter, MinTotalLotsFilter,
55-
MaxPricePerHourFilter, MinPricePerHourFilter
56+
MaxPricePerHourFilter, MinPricePerHourFilter,
57+
MinAvailableLotsFilter, MaxAvailableLotsFilter
5658
)
5759

5860
@admin.display(description='Пользователь', ordering='user_id')
@@ -104,16 +106,16 @@ def changelist_view(self, request, extra_context=None):
104106
user_id = request.GET.get('user_id')
105107
extra_context = {}
106108
if user_id:
107-
user = get_object_or_404(User, pk=user_id)
108-
extra_context.update({
109-
'title': f'Оплаты пользователя {user}'
110-
})
109+
try:
110+
extra_context['title'] = f'Оплаты {User.objects.get(pk=user_id)}'
111+
except Exception:
112+
pass
111113
parking_id = request.GET.get('parking_id')
112114
if parking_id:
113-
parking = get_object_or_404(Parking, pk=parking_id)
114-
extra_context.update({
115-
'subtitle': f'Оплаты парковки {parking}'
116-
})
115+
try:
116+
extra_context['subtitle'] = str(Parking.objects.get(pk=parking_id))
117+
except Exception:
118+
pass
117119
return super().changelist_view(request, extra_context)
118120

119121
def add_view(self, request, form_url='', extra_context=None):

application/pay_parking/paying/filters.py

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,62 +5,62 @@
55
)
66

77

8-
class MinCreatedAtFiler(MinFilter):
8+
class MinCreatedAtFilter(MinFilter):
99
parameter_name = 'min_created_at'
1010
field_name = 'created_at'
1111

1212

13-
class MaxCreatedAtFiler(MaxFilter):
13+
class MaxCreatedAtFilter(MaxFilter):
1414
parameter_name = 'max_created_at'
1515
field_name = 'created_at'
1616

1717

18-
class MinStartFiler(MinFilter):
18+
class MinStartFilter(MinFilter):
1919
parameter_name = 'min_start'
2020
field_name = 'start'
2121

2222

23-
class MaxStartFiler(MaxFilter):
23+
class MaxStartFilter(MaxFilter):
2424
parameter_name = 'max_start'
2525
field_name = 'end'
2626

2727

28-
class MinEndFiler(MinFilter):
28+
class MinEndFilter(MinFilter):
2929
parameter_name = 'min_end'
3030
field_name = 'end'
3131

3232

33-
class MaxEndFiler(MaxFilter):
33+
class MaxEndFilter(MaxFilter):
3434
parameter_name = 'max_end'
3535
field_name = 'end'
3636

3737

38-
class MinPriceFiler(MinFilter):
38+
class MinPriceFilter(MinFilter):
3939
parameter_name = 'min_price'
4040
field_name = 'price'
4141

4242

43-
class MaxPriceFiler(MaxFilter):
43+
class MaxPriceFilter(MaxFilter):
4444
parameter_name = 'max_price'
4545
field_name = 'price'
4646

4747

48-
class MinDurationFiler(MinHourDurationFilter):
48+
class MinDurationFilter(MinHourDurationFilter):
4949
parameter_name = 'min_duration'
5050
field_name = 'duration'
5151

5252

53-
class MaxDurationFiler(MaxHourDurationFilter):
53+
class MaxDurationFilter(MaxHourDurationFilter):
5454
parameter_name = 'max_duration'
5555
field_name = 'duration'
5656

5757

58-
class AddressFiler(ContainFilter):
58+
class AddressFilter(ContainFilter):
5959
parameter_name = 'address'
6060
field_name = 'parking_detail__address'
6161

6262

63-
class ParkingZoneFiler(IntergerExactFilter):
63+
class ParkingZoneFilter(IntergerExactFilter):
6464
parameter_name = 'parking_zone'
6565
field_name = 'parking_detail__parking_zone'
6666

@@ -95,6 +95,16 @@ class MaxTotalLotsFilter(MaxFilter):
9595
field_name = 'parking_detail__total_lots'
9696

9797

98+
class MinAvailableLotsFilter(MinFilter):
99+
parameter_name = 'min_available_lots'
100+
field_name = 'parking_detail__available_lots'
101+
102+
103+
class MaxAvailableLotsFilter(MaxFilter):
104+
parameter_name = 'max_available_lots'
105+
field_name = 'parking_detail__available_lots'
106+
107+
98108
class MinPricePerHourFilter(MinFilter):
99109
parameter_name = 'min_price_per_hour'
100110
field_name = 'parking_detail__price_per_hour'
@@ -105,36 +115,36 @@ class MaxPricePerHourFilter(MaxFilter):
105115
field_name = 'parking_detail__price_per_hour'
106116

107117

108-
class FirstNameFiler(ContainFilter):
118+
class FirstNameFilter(ContainFilter):
109119
parameter_name = 'first_name'
110120
field_name = 'user_detail__first_name'
111121

112122

113-
class SecondNameFiler(ContainFilter):
123+
class SecondNameFilter(ContainFilter):
114124
parameter_name = 'second_name'
115125
field_name = 'user_detail__second_name'
116126

117127

118-
class ThirdNameFiler(ContainFilter):
128+
class ThirdNameFilter(ContainFilter):
119129
parameter_name = 'third_name'
120130
field_name = 'user_detail__third_name'
121131

122132

123-
class EmailFiler(ContainFilter):
133+
class EmailFilter(ContainFilter):
124134
parameter_name = 'email'
125135
field_name = 'user_detail__email'
126136

127137

128-
class IsStaffFiler(BooleanExactFilter):
138+
class IsStaffFilter(BooleanExactFilter):
129139
parameter_name = 'is_staff'
130140
field_name = 'user_detail__is_staff'
131141

132142

133-
class UserIdFiler(ExactFilter):
143+
class UserIdFilter(ExactFilter):
134144
parameter_name = 'user_id'
135145
field_name = 'user_id'
136146

137147

138-
class ParkingIdFiler(ExactFilter):
139-
parameter_name = 'user_id'
140-
field_name = 'user_id'
148+
class ParkingIdFilter(ExactFilter):
149+
parameter_name = 'parking_id'
150+
field_name = 'parking_id'

application/pay_parking/paying/forms.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from django import forms
2-
from .models import Payment, Parking
2+
from .models import Payment, Parking, User
33
from django.core.validators import MinValueValidator, MaxValueValidator
44
from pay_parking.forms import FormWithFormsets
55
from .models import now
@@ -11,10 +11,6 @@
1111

1212

1313
class AdminPaymentForm(forms.ModelForm):
14-
# parking = forms.ModelChoiceField(
15-
# Parking.objects.all(),
16-
# widget=widgets.FilteredSelectMultiple('Парковка', False)
17-
# )
1814
class Meta:
1915
model = Payment
2016
fields = ('start', 'end', 'parking', 'user', )
@@ -139,6 +135,16 @@ class UserPaymentFilterForm(FormWithFormsets):
139135
validators=[MinValueValidator(0)],
140136
label='Макс. число мест'
141137
)
138+
min_available_lots = forms.IntegerField(
139+
validators=[MinValueValidator(0)],
140+
required=False,
141+
label='Мин. число свободных мест'
142+
)
143+
max_available_lots = forms.IntegerField(
144+
required=False,
145+
validators=[MinValueValidator(0)],
146+
label='Макс. число свободных мест'
147+
)
142148
min_price_per_hour = forms.DecimalField(
143149
max_digits=8, decimal_places=2,
144150
required=False,
@@ -196,6 +202,7 @@ class Meta:
196202
('Парковка', {
197203
'fields': (
198204
'address', 'parking_zone', 'min_total_lots', 'max_total_lots',
205+
'min_available_lots', 'max_available_lots',
199206
'min_price_per_hour', 'max_price_per_hour', 'min_longitude',
200207
'max_longitude', 'min_latitude', 'max_latitude'
201208
)
@@ -211,8 +218,16 @@ class PaymentFilterForm(UserPaymentFilterForm):
211218
max_length=30, required=False, label='Отчество')
212219
email = forms.CharField(max_length=254, required=False, label='Email')
213220
is_staff = forms.BooleanField(required=False, label='Сотрудник')
214-
user_id = forms.HiddenInput()
215-
parking_id = forms.HiddenInput()
221+
user_id = forms.ModelChoiceField(
222+
User.objects.all(),
223+
required=False, label='ID',
224+
widget=forms.TextInput()
225+
)
226+
parking_id = forms.ModelChoiceField(
227+
Parking.objects.all(),
228+
required=False, label='ID',
229+
widget=forms.TextInput()
230+
)
216231

217232
class Meta:
218233
fieldsets = (
@@ -235,10 +250,10 @@ class Meta:
235250
'fields': (
236251
'address', 'parking_zone', 'min_total_lots', 'max_total_lots',
237252
'min_price_per_hour', 'max_price_per_hour', 'min_longitude',
238-
'max_longitude', 'min_latitude', 'max_latitude'
253+
'max_longitude', 'min_latitude', 'max_latitude', 'parking_id'
239254
)
240255
}),
241256
('Пользователь', {
242-
'fields': ('first_name', 'second_name', 'third_name', 'email', 'is_staff')
257+
'fields': ('first_name', 'second_name', 'third_name', 'email', 'is_staff', 'user_id')
243258
}),
244259
)

0 commit comments

Comments
 (0)