Razmig Sarkissian 2 months ago
commit 1482f7f670
  1. 0
      api/__init__.py
  2. 22
      api/admin.py
  3. 7
      api/apps.py
  4. 24
      api/authentication.py
  5. 36
      api/migrations/0001_initial.py
  6. 0
      api/migrations/__init__.py
  7. 23
      api/models.py
  8. 4
      api/views.py
  9. 2
      padelclub_backend/settings.py
  10. 1
      requirements.txt
  11. 4
      tournaments/admin.py
  12. 56
      tournaments/filters.py

@ -0,0 +1,22 @@
from django.contrib import admin
from rest_framework_api_key.admin import APIKeyModelAdmin
from rest_framework_api_key.models import APIKey as DefaultAPIKey
from .models import APIKey
# Unregister the default APIKey admin
admin.site.unregister(DefaultAPIKey)
@admin.register(APIKey)
class APIKeyAdmin(APIKeyModelAdmin):
list_display = [*APIKeyModelAdmin.list_display, "user"]
list_filter = [*APIKeyModelAdmin.list_filter, "user"]
search_fields = [*APIKeyModelAdmin.search_fields, "user__username", "user__email"]
raw_id_fields = ['user']
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
# Make user field required
if 'user' in form.base_fields:
form.base_fields['user'].required = True
return form

@ -0,0 +1,7 @@
from django.apps import AppConfig
class ApiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'api'
verbose_name = 'API'

@ -0,0 +1,24 @@
from rest_framework_api_key.permissions import BaseHasAPIKey
from .models import APIKey
class HasAPIKey(BaseHasAPIKey):
model = APIKey
def has_permission(self, request, view):
# First check if we have a valid API key
has_api_key = super().has_permission(request, view)
if has_api_key:
# Get the API key from the request
key = self.get_key(request)
if key:
try:
api_key = APIKey.objects.get_from_key(key)
# Set the request.user to the user associated with the API key
request.user = api_key.user
return True
except APIKey.DoesNotExist:
pass
return False

@ -0,0 +1,36 @@
# Generated by Django 5.1 on 2025-09-17 07:49
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='APIKey',
fields=[
('id', models.CharField(editable=False, max_length=150, primary_key=True, serialize=False, unique=True)),
('prefix', models.CharField(editable=False, max_length=8, unique=True)),
('hashed_key', models.CharField(editable=False, max_length=150)),
('created', models.DateTimeField(auto_now_add=True, db_index=True)),
('name', models.CharField(default=None, help_text='A free-form name for the API key. Need not be unique. 50 characters max.', max_length=50)),
('revoked', models.BooleanField(blank=True, default=False, help_text='If the API key is revoked, clients cannot use it anymore. (This cannot be undone.)')),
('expiry_date', models.DateTimeField(blank=True, help_text='Once API key expires, clients cannot use it anymore.', null=True, verbose_name='Expires')),
('user', models.ForeignKey(help_text='The user this API key belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='api_keys', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'API Key',
'verbose_name_plural': 'API Keys',
'ordering': ('-created',),
'abstract': False,
},
),
]

@ -0,0 +1,23 @@
from django.db import models
from rest_framework_api_key.models import AbstractAPIKey
from tournaments.models import CustomUser
class APIKey(AbstractAPIKey):
"""
API Key model linked to a specific user.
This allows filtering API access based on the user associated with the API key.
"""
user = models.ForeignKey(
CustomUser,
on_delete=models.CASCADE,
related_name='api_keys',
help_text='The user this API key belongs to'
)
class Meta(AbstractAPIKey.Meta):
verbose_name = "API Key"
verbose_name_plural = "API Keys"
def __str__(self):
return f"API Key for {self.user.username}"

@ -4,6 +4,7 @@ from rest_framework.decorators import api_view, permission_classes
from rest_framework import status
from rest_framework.exceptions import MethodNotAllowed
from rest_framework.permissions import IsAuthenticated
from .authentication import HasAPIKey
from django.conf import settings
from django.http import Http404, HttpResponse, JsonResponse
@ -76,10 +77,11 @@ class ClubViewSet(SoftDeleteViewSet):
class TournamentSummaryViewSet(SoftDeleteViewSet):
queryset = Tournament.objects.all()
serializer_class = TournamentSummarySerializer
permission_classes = [HasAPIKey]
def get_queryset(self):
if self.request.user.is_anonymous:
return []
return Tournament.objects.none()
return self.queryset.filter(
Q(event__creator=self.request.user) | Q(related_user=self.request.user)

@ -37,6 +37,7 @@ INSTALLED_APPS = [
'tournaments',
'shop',
'biz',
'api',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
@ -50,6 +51,7 @@ INSTALLED_APPS = [
'channels_redis',
'django_filters',
'background_task',
'rest_framework_api_key',
]

@ -19,3 +19,4 @@ stripe==11.6.0
django-background-tasks==1.2.8
Pillow==10.2.0
playwright==1.40.0
djangorestframework-api-key==3.1.0

@ -11,7 +11,7 @@ from datetime import datetime, timedelta # Add this import
from .models import Club, TeamScore, Tournament, CustomUser, Event, Round, GroupStage, Match, TeamRegistration, PlayerRegistration, Purchase, Court, DateInterval, FailedApiCall, Log, DeviceToken, DrawLog, UnregisteredTeam, UnregisteredPlayer, Image
from .forms import CustomUserCreationForm, CustomUserChangeForm
from .filters import TeamScoreTournamentListFilter, MatchTournamentListFilter, SimpleTournamentListFilter, MatchTypeListFilter, SimpleIndexListFilter, StartDateRangeFilter
from .filters import TeamScoreTournamentListFilter, MatchTournamentListFilter, SimpleTournamentListFilter, MatchTypeListFilter, SimpleIndexListFilter, StartDateRangeFilter, UserWithEventsFilter, UserWithPurchasesFilter, UserWithProspectFilter
from sync.admin import SyncedObjectAdmin
@ -23,7 +23,7 @@ class CustomUserAdmin(UserAdmin):
filter_horizontal = ('clubs',)
list_display = ['email', 'first_name', 'last_name', 'username', 'date_joined', 'latest_event_club_name', 'is_active', 'event_count', 'origin', 'registration_payment_mode', 'licence_id']
list_filter = ['is_active', 'origin']
list_filter = ['is_active', 'origin', UserWithEventsFilter, UserWithPurchasesFilter, UserWithProspectFilter]
ordering = ['-date_joined']
raw_id_fields = ['agents']
fieldsets = [

@ -1,6 +1,6 @@
from django.contrib import admin
from .models import Tournament, Match
from django.db.models import Q
from django.db.models import Q, Count
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
from datetime import timedelta
@ -135,3 +135,57 @@ class StartDateRangeFilter(admin.SimpleListFilter):
start_date__gte=today - timedelta(days=3),
start_date__lte=today + timedelta(days=3)
)
class UserWithEventsFilter(admin.SimpleListFilter):
title = _('has events')
parameter_name = 'has_events'
def lookups(self, request, model_admin):
return (
('yes', _('Has events')),
('no', _('No events')),
)
def queryset(self, request, queryset):
if self.value() == 'yes':
return queryset.annotate(events_count=Count('events')).filter(events_count__gt=0)
elif self.value() == 'no':
return queryset.annotate(events_count=Count('events')).filter(events_count=0)
return queryset
class UserWithPurchasesFilter(admin.SimpleListFilter):
title = _('has purchases')
parameter_name = 'has_purchases'
def lookups(self, request, model_admin):
return (
('yes', _('Has purchases')),
('no', _('No purchases')),
)
def queryset(self, request, queryset):
if self.value() == 'yes':
return queryset.annotate(purchases_count=Count('purchases')).filter(purchases_count__gt=0)
elif self.value() == 'no':
return queryset.annotate(purchases_count=Count('purchases')).filter(purchases_count=0)
return queryset
class UserWithProspectFilter(admin.SimpleListFilter):
title = _('has prospect')
parameter_name = 'has_prospect'
def lookups(self, request, model_admin):
return (
('yes', _('Has prospect')),
('no', _('No prospect')),
)
def queryset(self, request, queryset):
from biz.models import Prospect
if self.value() == 'yes':
prospect_emails = Prospect.objects.values_list('email', flat=True).filter(email__isnull=False)
return queryset.filter(email__in=prospect_emails)
elif self.value() == 'no':
prospect_emails = Prospect.objects.values_list('email', flat=True).filter(email__isnull=False)
return queryset.exclude(email__in=prospect_emails)
return queryset

Loading…
Cancel
Save