extensions-website/extensions/views/mixins.py
Oleg Komarov ac49e9d5f6 Refactor Extension and Version: explicit constructors from File (#191)
This change solves a blocker for an upcoming drop of Version.file field:

calculating `Extension.latest_version` relied on having `VersionFiles` already
populated, but it was triggered on `Version.save()`, which happened before
the cross table could be populated.

One functional change is that a Version object is now created in
NewVersionView, immediately after the File is saved, and not in
NewVersionFinalizeView, as before.

Tests are rewritten to require only FileFactory, the Extension and Version
objects are created as it is done in the actual production code.
This loses a bit on the ergonomics of factories, but overall makes the state of
test objects more consistent, often simplifying the test code.

VersionFactory is no longer needed, and has been cleaned up.
ExtensionFactory is only used in construct_fake_notifications, and could also
be replaced with an in-memory object constructed manually.

Reviewed-on: #191
Reviewed-by: Anna Sirota <annasirota@noreply.localhost>
2024-06-20 14:40:39 +02:00

65 lines
2.1 KiB
Python

from django.contrib.auth.mixins import UserPassesTestMixin
from django.shortcuts import get_object_or_404
from extensions.models import Extension
from files.models import File
class OwnsFileMixin(UserPassesTestMixin):
def dispatch(self, *args, **kwargs):
self.file = get_object_or_404(File, pk=self.kwargs['pk'])
return super().dispatch(*args, **kwargs)
def test_func(self) -> bool:
return self.file.user == self.request.user
class ExtensionQuerysetMixin:
"""Add reusable methods to class-based views handling Extensions."""
def get_extension_queryset(self):
"""Return queryset with unlisted add-ons for logged in users under certain conditions."""
if self.request.user.is_staff:
return Extension.objects.all()
if self.request.user.is_authenticated:
return Extension.objects.listed_or_authored_by(self.request.user)
return Extension.objects.listed
class MaintainedExtensionMixin:
"""Fetch an extension by slug if current user is a maintainer."""
def dispatch(self, *args, **kwargs):
self.extension = get_object_or_404(
Extension.objects.authored_by(self.request.user),
slug=self.kwargs['slug'],
)
return super().dispatch(*args, **kwargs)
class ListedExtensionMixin:
"""Fetch a publicly listed extension by slug in the URL before dispatching the view."""
def dispatch(self, *args, **kwargs):
self.extension = get_object_or_404(
Extension.objects.listed.prefetch_related('authors', 'ratings'),
slug=self.kwargs['slug'],
)
return super().dispatch(*args, **kwargs)
class ExtensionMixin:
"""Fetch an extension by slug in the URL before dispatching the view."""
def dispatch(self, *args, **kwargs):
self.extension = get_object_or_404(Extension, slug=self.kwargs['slug'])
return super().dispatch(*args, **kwargs)
class DraftVersionMixin:
"""Fetch the version object which is being edited as a draft."""
def dispatch(self, *args, **kwargs):
self.version = self.extension.versions.first()
return super().dispatch(*args, **kwargs)