Compare commits
2 commits
4112069cac
...
cf3c84b8d9
Author | SHA1 | Date | |
---|---|---|---|
Víðir Valberg Guðmundsson | cf3c84b8d9 | ||
Víðir Valberg Guðmundsson | 1b65558608 |
|
@ -1,5 +1,5 @@
|
||||||
default_language_version:
|
default_language_version:
|
||||||
python: python3.11
|
python: python3.12
|
||||||
exclude: ^.*\b(migrations)\b.*$
|
exclude: ^.*\b(migrations)\b.*$
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
@ -16,45 +16,22 @@ repos:
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||||
rev: 'v0.1.11'
|
rev: 'v0.3.0'
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
args:
|
args:
|
||||||
- --fix
|
- --fix
|
||||||
- repo: https://github.com/asottile/reorder_python_imports
|
- id: ruff-format
|
||||||
rev: v3.12.0
|
|
||||||
hooks:
|
|
||||||
- id: reorder-python-imports
|
|
||||||
args:
|
|
||||||
- --py310-plus
|
|
||||||
- --application-directories=.:src
|
|
||||||
exclude: migrations/
|
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: v3.15.0
|
rev: v3.15.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args:
|
args:
|
||||||
- --py311-plus
|
- --py311-plus
|
||||||
exclude: migrations/
|
exclude: migrations/
|
||||||
- repo: https://github.com/adamchainz/django-upgrade
|
- repo: https://github.com/adamchainz/django-upgrade
|
||||||
rev: 1.15.0
|
rev: 1.16.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: django-upgrade
|
- id: django-upgrade
|
||||||
args:
|
args:
|
||||||
- --target-version=4.1
|
- --target-version=5.0
|
||||||
- repo: https://github.com/asottile/yesqa
|
|
||||||
rev: v1.5.0
|
|
||||||
hooks:
|
|
||||||
- id: yesqa
|
|
||||||
- repo: https://github.com/asottile/add-trailing-comma
|
|
||||||
rev: v3.1.0
|
|
||||||
hooks:
|
|
||||||
- id: add-trailing-comma
|
|
||||||
- repo: https://github.com/hadialqattan/pycln
|
|
||||||
rev: v2.4.0
|
|
||||||
hooks:
|
|
||||||
- id: pycln
|
|
||||||
- repo: https://github.com/psf/black
|
|
||||||
rev: 23.12.1
|
|
||||||
hooks:
|
|
||||||
- id: black
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM python:3.11-slim-bullseye
|
FROM python:3.12-slim-bullseye
|
||||||
|
|
||||||
ENV PYTHONFAULTHANDLER=1 \
|
ENV PYTHONFAULTHANDLER=1 \
|
||||||
PYTHONUNBUFFERED=1 \
|
PYTHONUNBUFFERED=1 \
|
||||||
|
|
|
@ -104,3 +104,26 @@ follow_imports = "normal"
|
||||||
[[tool.mypy.overrides]]
|
[[tool.mypy.overrides]]
|
||||||
module = "tests.*"
|
module = "tests.*"
|
||||||
allow_untyped_defs = true
|
allow_untyped_defs = true
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
target-version = "py312"
|
||||||
|
extend-exclude = [
|
||||||
|
".git",
|
||||||
|
"__pycache__",
|
||||||
|
]
|
||||||
|
line-length = 120
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
select = ["ALL"]
|
||||||
|
ignore = [
|
||||||
|
"G004", # Logging statement uses f-string
|
||||||
|
"ANN101", # Missing type annotation for `self` in method
|
||||||
|
"ANN102", # Missing type annotation for `cls` in classmethod
|
||||||
|
"EM101", # Exception must not use a string literal, assign to variable first
|
||||||
|
"EM102", # Exception must not use a f-string literal, assign to variable first
|
||||||
|
"COM812", # missing-trailing-comma (https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules)
|
||||||
|
"ISC001", # single-line-implicit-string-concatenation (https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules)
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff.lint.isort]
|
||||||
|
force-single-line = true
|
||||||
|
|
|
@ -7,7 +7,6 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
@ -99,9 +98,7 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"vat",
|
"vat",
|
||||||
djmoney.models.fields.MoneyField(
|
djmoney.models.fields.MoneyField(decimal_places=2, max_digits=16, verbose_name="VAT"),
|
||||||
decimal_places=2, max_digits=16, verbose_name="VAT"
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
("is_paid", models.BooleanField(default=False, verbose_name="is paid")),
|
("is_paid", models.BooleanField(default=False, verbose_name="is paid")),
|
||||||
(
|
(
|
||||||
|
|
|
@ -17,8 +17,7 @@ class CreatedModifiedAbstract(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Account(CreatedModifiedAbstract):
|
class Account(CreatedModifiedAbstract):
|
||||||
"""
|
"""This is the model where we can give access to several users, such that they
|
||||||
This is the model where we can give access to several users, such that they
|
|
||||||
can decide which account to use to pay for something.
|
can decide which account to use to pay for something.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -30,8 +29,7 @@ class Account(CreatedModifiedAbstract):
|
||||||
|
|
||||||
|
|
||||||
class Transaction(CreatedModifiedAbstract):
|
class Transaction(CreatedModifiedAbstract):
|
||||||
"""
|
"""Tracks in and outgoing events of an account. When an order is received, an
|
||||||
Tracks in and outgoing events of an account. When an order is received, an
|
|
||||||
amount is subtracted, when a payment is received, an amount is added.
|
amount is subtracted, when a payment is received, an amount is added.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -50,8 +48,7 @@ class Transaction(CreatedModifiedAbstract):
|
||||||
|
|
||||||
|
|
||||||
class Order(CreatedModifiedAbstract):
|
class Order(CreatedModifiedAbstract):
|
||||||
"""
|
"""Scoped out: Contents of invoices will have to be tracked either here or in
|
||||||
Scoped out: Contents of invoices will have to be tracked either here or in
|
|
||||||
a separate Invoice model. This is undecided because we are not generating
|
a separate Invoice model. This is undecided because we are not generating
|
||||||
invoices at the moment.
|
invoices at the moment.
|
||||||
"""
|
"""
|
||||||
|
@ -91,7 +88,7 @@ class Order(CreatedModifiedAbstract):
|
||||||
verbose_name = pgettext_lazy("accounting term", "Order")
|
verbose_name = pgettext_lazy("accounting term", "Order")
|
||||||
verbose_name_plural = pgettext_lazy("accounting term", "Orders")
|
verbose_name_plural = pgettext_lazy("accounting term", "Orders")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return f"Order ID {self.display_id}"
|
return f"Order ID {self.display_id}"
|
||||||
|
|
||||||
|
|
||||||
|
@ -116,7 +113,7 @@ class Payment(CreatedModifiedAbstract):
|
||||||
description=order.description,
|
description=order.description,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return f"Payment ID {self.display_id}"
|
return f"Payment ID {self.display_id}"
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -8,8 +8,8 @@ from . import models
|
||||||
# do stuff
|
# do stuff
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db()
|
||||||
def test_balance():
|
def test_balance() -> None:
|
||||||
user = User.objects.create_user("test", "lala@adas.com", "1234")
|
user = User.objects.create_user("test", "lala@adas.com", "1234")
|
||||||
account = models.Account.objects.create(owner=user)
|
account = models.Account.objects.create(owner=user)
|
||||||
assert account.balance == 0
|
assert account.balance == 0
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
"""
|
"""Membership application.
|
||||||
Membership application
|
|
||||||
======================
|
======================
|
||||||
|
|
||||||
This application's domain relate to organizational structures and
|
This application's domain relate to organizational structures and
|
||||||
|
|
|
@ -5,7 +5,7 @@ from django.db.models.signals import post_migrate
|
||||||
class MembershipConfig(AppConfig):
|
class MembershipConfig(AppConfig):
|
||||||
name = "membership"
|
name = "membership"
|
||||||
|
|
||||||
def ready(self):
|
def ready(self) -> None:
|
||||||
from .permissions import persist_permissions
|
from .permissions import persist_permissions
|
||||||
|
|
||||||
post_migrate.connect(persist_permissions, sender=self)
|
post_migrate.connect(persist_permissions, sender=self)
|
||||||
|
|
|
@ -7,7 +7,6 @@ from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
import django.contrib.postgres.constraints
|
import django.contrib.postgres.constraints
|
||||||
import django.contrib.postgres.fields.ranges
|
import django.contrib.postgres.fields.ranges
|
||||||
from django.db import migrations, models
|
from django.db import migrations
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
("membership", "0001_initial"),
|
("membership", "0001_initial"),
|
||||||
]
|
]
|
||||||
|
@ -34,9 +34,7 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"period",
|
"period",
|
||||||
django.contrib.postgres.fields.ranges.DateRangeField(
|
django.contrib.postgres.fields.ranges.DateRangeField(verbose_name="period"),
|
||||||
verbose_name="period"
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Generated by Django 4.1 on 2023-01-02 21:05
|
# Generated by Django 4.1 on 2023-01-02 21:05
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
("membership", "0002_subscriptionperiod_remove_membership_period_and_more"),
|
("membership", "0002_subscriptionperiod_remove_membership_period_and_more"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# Generated by Django 4.1 on 2023-01-02 21:06
|
# Generated by Django 4.1 on 2023-01-02 21:06
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
("membership", "0003_membership_period"),
|
("membership", "0003_membership_period"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,22 +4,20 @@ from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('auth', '0012_alter_user_first_name_max_length'),
|
("auth", "0012_alter_user_first_name_max_length"),
|
||||||
('membership', '0004_alter_membership_period'),
|
("membership", "0004_alter_membership_period"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Member',
|
name="Member",
|
||||||
fields=[
|
fields=[],
|
||||||
],
|
|
||||||
options={
|
options={
|
||||||
'proxy': True,
|
"proxy": True,
|
||||||
'indexes': [],
|
"indexes": [],
|
||||||
'constraints': [],
|
"constraints": [],
|
||||||
},
|
},
|
||||||
bases=('auth.user',),
|
bases=("auth.user",),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,7 +5,6 @@ from django.contrib.postgres.fields import RangeOperators
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from utils.mixins import CreatedModifiedAbstract
|
from utils.mixins import CreatedModifiedAbstract
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,9 +34,7 @@ class Member(User):
|
||||||
|
|
||||||
|
|
||||||
class SubscriptionPeriod(CreatedModifiedAbstract):
|
class SubscriptionPeriod(CreatedModifiedAbstract):
|
||||||
"""
|
"""Denotes a period for which members should pay their membership fee for."""
|
||||||
Denotes a period for which members should pay their membership fee for.
|
|
||||||
"""
|
|
||||||
|
|
||||||
period = DateRangeField(verbose_name=_("period"))
|
period = DateRangeField(verbose_name=_("period"))
|
||||||
|
|
||||||
|
@ -51,16 +48,12 @@ class SubscriptionPeriod(CreatedModifiedAbstract):
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return (
|
return f"{self.period.lower} - {self.period.upper or _('next general assembly')}"
|
||||||
f"{self.period.lower} - {self.period.upper or _('next general assembly')}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Membership(CreatedModifiedAbstract):
|
class Membership(CreatedModifiedAbstract):
|
||||||
"""
|
"""Tracks that a user has membership of a given type for a given period."""
|
||||||
Tracks that a user has membership of a given type for a given period.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class QuerySet(models.QuerySet):
|
class QuerySet(models.QuerySet):
|
||||||
def for_member(self, member: Member):
|
def for_member(self, member: Member):
|
||||||
|
@ -101,13 +94,12 @@ class Membership(CreatedModifiedAbstract):
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return f"{self.user} - {self.period}"
|
return f"{self.user} - {self.period}"
|
||||||
|
|
||||||
|
|
||||||
class MembershipType(CreatedModifiedAbstract):
|
class MembershipType(CreatedModifiedAbstract):
|
||||||
"""
|
"""Models membership types. Currently only a name, but will in the future
|
||||||
Models membership types. Currently only a name, but will in the future
|
|
||||||
possibly contain more information like fees.
|
possibly contain more information like fees.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -117,5 +109,5 @@ class MembershipType(CreatedModifiedAbstract):
|
||||||
|
|
||||||
name = models.CharField(verbose_name=_("name"), max_length=64)
|
name = models.CharField(verbose_name=_("name"), max_length=64)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
|
|
|
@ -7,7 +7,7 @@ from django.utils.translation import gettext_lazy as _
|
||||||
PERMISSIONS = []
|
PERMISSIONS = []
|
||||||
|
|
||||||
|
|
||||||
def persist_permissions(sender, **kwargs):
|
def persist_permissions(sender, **kwargs) -> None:
|
||||||
for permission in PERMISSIONS:
|
for permission in PERMISSIONS:
|
||||||
permission.persist_permission()
|
permission.persist_permission()
|
||||||
|
|
||||||
|
@ -23,10 +23,10 @@ class Permission:
|
||||||
PERMISSIONS.append(self)
|
PERMISSIONS.append(self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path(self):
|
def path(self) -> str:
|
||||||
return f"{self.app_label}.{self.codename}"
|
return f"{self.app_label}.{self.codename}"
|
||||||
|
|
||||||
def persist_permission(self):
|
def persist_permission(self) -> None:
|
||||||
content_type, _ = ContentType.objects.get_or_create(
|
content_type, _ = ContentType.objects.get_or_create(
|
||||||
app_label=self.app_label,
|
app_label=self.app_label,
|
||||||
model=self.model,
|
model=self.model,
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django_view_decorator import namespaced_decorator_factory
|
from django_view_decorator import namespaced_decorator_factory
|
||||||
|
from utils.view_utils import RowAction
|
||||||
|
from utils.view_utils import render
|
||||||
|
from utils.view_utils import render_list
|
||||||
|
|
||||||
from .permissions import ADMINISTRATE_MEMBERS
|
from .permissions import ADMINISTRATE_MEMBERS
|
||||||
from .selectors import get_member
|
from .selectors import get_member
|
||||||
from .selectors import get_members
|
from .selectors import get_members
|
||||||
from .selectors import get_memberships
|
from .selectors import get_memberships
|
||||||
from .selectors import get_subscription_periods
|
from .selectors import get_subscription_periods
|
||||||
from utils.view_utils import render
|
|
||||||
from utils.view_utils import render_list
|
|
||||||
from utils.view_utils import RowAction
|
|
||||||
|
|
||||||
|
|
||||||
member_view = namespaced_decorator_factory(namespace="member", base_path="membership")
|
member_view = namespaced_decorator_factory(namespace="member", base_path="membership")
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
"""URLs for the membersystem"""
|
"""URLs for the membersystem."""
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import include
|
from django.urls import include
|
||||||
|
@ -15,4 +15,5 @@ if settings.DEBUG:
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("__debug__/", include("debug_toolbar.urls")),
|
path("__debug__/", include("debug_toolbar.urls")),
|
||||||
path("__reload__/", include("django_browser_reload.urls")),
|
path("__reload__/", include("django_browser_reload.urls")),
|
||||||
] + urlpatterns
|
*urlpatterns,
|
||||||
|
]
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from django_view_decorator import view
|
from django_view_decorator import view
|
||||||
|
|
||||||
from utils.view_utils import render
|
from utils.view_utils import render
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
"""
|
"""WSGI config for membersystem project.
|
||||||
WSGI config for membersystem project.
|
|
||||||
|
|
||||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ register = template.Library()
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def active_path(context, path_name, class_name) -> str | None:
|
def active_path(context, path_name, class_name) -> str | None:
|
||||||
"""Return the given class name if the current path matches the given path name."""
|
"""Return the given class name if the current path matches the given path name."""
|
||||||
|
|
||||||
path = reverse(path_name)
|
path = reverse(path_name)
|
||||||
request_path = context.get("request").path
|
request_path = context.get("request").path
|
||||||
|
|
||||||
|
@ -19,3 +18,4 @@ def active_path(context, path_name, class_name) -> str | None:
|
||||||
|
|
||||||
if is_path or is_base_path:
|
if is_path or is_base_path:
|
||||||
return class_name
|
return class_name
|
||||||
|
return None
|
||||||
|
|
|
@ -1,23 +1,24 @@
|
||||||
import contextlib
|
import contextlib
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from django.contrib.sites.shortcuts import get_current_site
|
from django.contrib.sites.shortcuts import get_current_site
|
||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from django.db.models import Model
|
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from zen_queries import queries_disabled
|
from zen_queries import queries_disabled
|
||||||
from zen_queries import render as zen_queries_render
|
from zen_queries import render as zen_queries_render
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from django.db.models import Model
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Row:
|
class Row:
|
||||||
"""
|
"""A row in a table."""
|
||||||
A row in a table.
|
|
||||||
"""
|
|
||||||
|
|
||||||
data: dict[str, str]
|
data: dict[str, str]
|
||||||
actions: list[dict[str, str]]
|
actions: list[dict[str, str]]
|
||||||
|
@ -25,18 +26,14 @@ class Row:
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class RowAction:
|
class RowAction:
|
||||||
"""
|
"""An action that can be performed on a row in a table."""
|
||||||
An action that can be performed on a row in a table.
|
|
||||||
"""
|
|
||||||
|
|
||||||
label: str
|
label: str
|
||||||
url_name: str
|
url_name: str
|
||||||
url_kwargs: dict[str, str]
|
url_kwargs: dict[str, str]
|
||||||
|
|
||||||
def render(self, obj) -> dict[str, str]:
|
def render(self, obj) -> dict[str, str]:
|
||||||
"""
|
"""Render the action as a dictionary for the given object."""
|
||||||
Render the action as a dictionary for the given object.
|
|
||||||
"""
|
|
||||||
url = reverse(
|
url = reverse(
|
||||||
self.url_name,
|
self.url_name,
|
||||||
kwargs={key: getattr(obj, value) for key, value in self.url_kwargs.items()},
|
kwargs={key: getattr(obj, value) for key, value in self.url_kwargs.items()},
|
||||||
|
@ -50,14 +47,11 @@ def render_list(
|
||||||
entity_name_plural: str,
|
entity_name_plural: str,
|
||||||
objects: list["Model"],
|
objects: list["Model"],
|
||||||
columns: list[tuple[str, str]],
|
columns: list[tuple[str, str]],
|
||||||
row_actions: list[RowAction] = None,
|
row_actions: list[RowAction] | None = None,
|
||||||
list_actions: list[tuple[str, str]] = None,
|
list_actions: list[tuple[str, str]] | None = None,
|
||||||
paginate_by: int = None,
|
paginate_by: int | None = None,
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
"""
|
"""Render a list of objects with a table."""
|
||||||
Render a list of objects with a table.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# TODO: List actions
|
# TODO: List actions
|
||||||
|
|
||||||
total_count = len(objects)
|
total_count = len(objects)
|
||||||
|
@ -107,16 +101,12 @@ def render_list(
|
||||||
|
|
||||||
|
|
||||||
def base_context(request: HttpRequest) -> dict[str, Any]:
|
def base_context(request: HttpRequest) -> dict[str, Any]:
|
||||||
"""
|
"""Return a base context for all views."""
|
||||||
Return a base context for all views.
|
|
||||||
"""
|
|
||||||
return {"site": get_current_site(request)}
|
return {"site": get_current_site(request)}
|
||||||
|
|
||||||
|
|
||||||
def render(request, template_name, context=None):
|
def render(request, template_name, context=None):
|
||||||
"""
|
"""Render a template with a base context."""
|
||||||
Render a template with a base context.
|
|
||||||
"""
|
|
||||||
if context is None:
|
if context is None:
|
||||||
context = {}
|
context = {}
|
||||||
context = base_context(request) | context
|
context = base_context(request) | context
|
||||||
|
|
Loading…
Reference in a new issue