Pertama-tama, terima kasih telah mempertahankan paket yang luar biasa ini 👍
Saya baru saja memperbarui Django-filter 1.1.0 ke 2.0.0 dan sekarang CBV berikut gagal saat startup Django:
class ForventetRegnskab(ABC, LoginRequiredMixin, UserPassesTestMixin, FilterView):
"""
Abstract base class for all "brugervisninger".
Each subclass must set the appropriate filterset_class and queryset.
"""
login_url = 'home'
raise_exception = True
context_object_name = 'context'
template_name = 'forventet_regnskab/content.html'
<strong i="7">@property</strong>
<strong i="8">@abstractmethod</strong>
def filterset_class(self) -> Optional[FilterView.filterset_class]:
pass
<strong i="9">@property</strong>
<strong i="10">@abstractmethod</strong>
def queryset(self):
pass
<strong i="11">@has_sted1_permission</strong>
def user_passes_test(self, request, kwargs) -> bool:
"""
Deny a request with a permission error if the method returns False.
:param request: The request object
:param kwargs: self.kwargs
"""
pass
def test_func(self):
return self.user_passes_test(self.request, self.kwargs)
def get_context_data(self, **kwargs):
context = super(ForventetRegnskab, self).get_context_data(**kwargs)
filter_object = self.filterset_class(self.request.GET, queryset=self.get_queryset())
# Add context variables
context['title'] = constants.FORVENTET_REGNSKAB_TITLE
context['filter_sum'] = self.filter_sum(filter_queryset=filter_object.qs)
return context
def get_queryset(self):
aar = self.request.GET.get('aar')
maaned = self.request.GET.get('maaned')
kasse = self.request.GET.get('kasse')
if aar and maaned and kasse:
query = self.queryset.filter(sted1=self.kwargs['sted1']) \
.annotate(
forbrugsprocent=Case(
When(Q(korrigeret_budget__gt=0),
then=(F('akkumuleret_regnskab') / F('korrigeret_budget')) * 100),
default=0,
output_field=IntegerField()),
mer_mindre_forbrug=Func(
F('forventet_regnskab'), function='ABS') - Func(F('korrigeret_budget'), function='ABS'))
else:
query = self.queryset.none()
return query
<strong i="12">@staticmethod</strong>
def filter_sum(filter_queryset: QuerySet) -> QuerySet:
"""
Aggregate the filtered queryset.
"""
filter_sum = filter_queryset.aggregate(
korrigeret_budget_sum=Sum('korrigeret_budget'),
regnskab_sum=Sum('regnskab'),
akkumuleret_regnskab_sum=Sum('akkumuleret_regnskab'),
forventet_regnskab_sum=Sum('forventet_regnskab'),
forbrugsprocent_sum=Case(
When(Q(korrigeret_budget_sum__gt=0),
then=(Sum('akkumuleret_regnskab') / Sum('korrigeret_budget')) * 100),
default=0,
output_field=IntegerField()),
mer_mindre_forbrug_sum=Func(
Sum('forventet_regnskab'), function='ABS') - Func(Sum('korrigeret_budget'), function='ABS'))
return filter_sum
Setiap subclass mengimplementasikan filterset_class dan querysetnya sendiri. Berikut ini contohnya:
class SpecialBrugervisning(ForventetRegnskab):
filterset_class = filters.SpecialBrugervisning
queryset = models.SpecialBrugervisning.objects.all()
Saya mendapatkan kesalahan ini saat startup:
Berkas "[...]\oekonomistyring\forventet_regnskab\views.py", baris 18, di
kelas ForventetRegnskab(ABC, LoginRequiredMixin, UserPassesTestMixin, FilterView):
TypeError: konflik metaclass: metaclass dari kelas turunan harus merupakan subclass (tidak ketat) dari metaclass dari semua basisnya
Hmmm... !
Apakah perilaku ini diinginkan atau ini bug?
Apa yang terjadi jika Anda menjatuhkan warisan ABC
?
(Lebih lanjut: adakah kemungkinan Anda dapat mengurangi ini menjadi contoh minimal sehingga saya dapat bermain sendiri?)
Hai! Ia bekerja tanpa warisan ABC 👍
Contoh minimal:
class ForventetRegnskabNoABC(LoginRequiredMixin, UserPassesTestMixin, FilterView):
login_url = 'home'
raise_exception = True
context_object_name = 'context'
template_name = 'content.html'
filterset_class = filters.my_filterset
queryset = models.my_queryset
def test_func(self):
return True
def get_context_data(self, **kwargs):
context = super(ForventetRegnskabNoABC, self).get_context_data(**kwargs)
filter_object = self.filterset_class(self.request.GET, queryset=self.get_queryset())
# Add context variables
context['filter_sum'] = self.filter_sum(filter_queryset=filter_object.qs)
return context
def get_queryset(self):
return self.queryset
<strong i="6">@staticmethod</strong>
def filter_sum(filter_queryset: QuerySet) -> QuerySet:
filter_sum = filter_queryset.aggregate()
return filter_sum
Contoh minimal untuk membuat ulang TypeError
adalah:
class Foo(abc.ABC, FilterView):
pass
Ini tidak terlihat spesifik untuk Django-filter, lebih dari itu sebuah kelas tidak dapat mewarisi dua metaclass yang terpisah.
class AMeta(type): pass
class BMeta(type): pass
class A(metaclass=AMeta): pass
class B(metaclass=BMeta): pass
# fails with metaclass error
class C(A, B): pass
# passes
class CMeta(AMeta, BMeta): pass
class C(A, B, metaclass=CMeta): pass
Perubahan yang relevan dalam 2.x adalah bahwa FilterMixin
menggunakan FilterMixinRenames
metaclass, menciptakan konflik dengan ABCMeta
. Anda harus dapat membuat metclass tanpa secara langsung merujuk ke metaclass tampilan.
```python
kelas MyMeta(ABCMeta, ketik(FilterView)):
lulus
kelas ForventetRegnskab(LoginRequiredMixin, UserPassesTestMixin, FilterView, metaclass=MyMeta):
lulus
``
Terima kasih atas tulisannya @rpkilby!
Hai kalian berdua!
@rpkilby jawaban yang sangat jelas - dan solusi Anda berfungsi sebagai catatan! 🥇
Semua yang terbaik, Henrik
Halo lagi!
Saya harap Anda punya waktu untuk mengklarifikasi satu hal: Mengapa Anda menggunakan type(FilterView) dan bukan hanya FilterView?
Apakah akan ditafsirkan sebagai "metaclass MyABC IS metaclass FilterView"?
Jenis kelas adalah metaclass-nya. yaitu, metaclass default adalah type
.
type(FilterView) membuat metaclass FilterView
Tidak persis, itu hanya mendapatkan metaclass (tidak membuat metaclass baru ). Ini adalah alternatif untuk mereferensikan FilterMixinRenames
secara eksplisit.
Kata kunci digunakan untuk menetapkan 1) sebagai satu-satunya metaclass untuk semua subclass?
Itu akan berlaku untuk kelas dan subkelasnya, kecuali diganti oleh metaclass lain.
Terima kasih sekali lagi, itu membantu.
Segala sesuatu di Python adalah objek, dan semuanya dibangun dari kelas. yaitu, kelas dari sebuah instance adalah kelasnya, kelas dari sebuah kelas adalah metaclass-nya. Satu-satunya kasus khusus adalah basis type
, yang memiliki tipe type
.
Jika Anda mengatakan bahwa suatu kelas tidak dapat mewarisi dua kelas meta yang terpisah, masuk akal untuk "menggabungkan" mereka menjadi satu dan menggunakannya dalam subkelas?
saya salah ketik. Kelas tidak dapat dibuat dari dua kelas meta. Namun, itu dapat dibuat dari metaclass yang mewarisi beberapa metaclass lainnya. Ini mirip dengan bagaimana Anda tidak dapat membuat instance objek dengan dua kelas. Anda perlu membuat satu kelas yang mewarisi keduanya dan membuat instance objek dari itu. Pembuatan kelas adalah dengan cara yang sama.
Saya berasumsi bahwa metaclass FilterClass diwarisi dari FilterMixinRenames...
Metaclassnya adalah FilterMixinRenames
. Anda dapat memverifikasi ini dengan type(FilterView)
.
Terima kasih! Semuanya masuk akal sekarang - ya, saya melakukan pengecekan tipe yang ditambahkan - ini bukan sihir :santai:
Komentar yang paling membantu
Ini tidak terlihat spesifik untuk Django-filter, lebih dari itu sebuah kelas tidak dapat mewarisi dua metaclass yang terpisah.
Perubahan yang relevan dalam 2.x adalah bahwa
FilterMixin
menggunakanFilterMixinRenames
metaclass, menciptakan konflik denganABCMeta
. Anda harus dapat membuat metclass tanpa secara langsung merujuk ke metaclass tampilan.```python
kelas MyMeta(ABCMeta, ketik(FilterView)):
lulus
kelas ForventetRegnskab(LoginRequiredMixin, UserPassesTestMixin, FilterView, metaclass=MyMeta):
lulus
``