Compare commits
10 commits
f21f156127
...
709ee8c454
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
709ee8c454 | ||
|
|
53786a7a4a | ||
|
|
4dca5cea43 | ||
|
|
2e8f58968b | ||
|
|
0a735c1ebd | ||
|
|
b14482e383 | ||
|
|
957b2705f3 | ||
|
|
579e959b1b | ||
|
|
623d98de5b | ||
|
|
2ba7a2e1e9 |
12 changed files with 98 additions and 23 deletions
|
|
@ -27,4 +27,12 @@ def get_proper_elided_page_range(p, number, on_each_side=2, on_ends=2):
|
|||
|
||||
@register.filter
|
||||
def get_item(dictionary, key):
|
||||
return dictionary.get(key)
|
||||
return dictionary.get(key)
|
||||
|
||||
@register.simple_tag
|
||||
def get_filter_link(request, key, value):
|
||||
qs: str = request.GET.urlencode()
|
||||
qs_dict = request.GET.dict()
|
||||
qs_dict.update({key: value})
|
||||
return '&'.join(f'{k}={v}' for k, v in qs_dict.items())
|
||||
# return 'page={{ i }}{% for fil, fil_value in filter.data.items %}{% if fil != 'page' %}&{{fil}}={{fil_value}}{% endif %}{% endfor %}">{{ i }}</a></li>
|
||||
|
|
|
|||
26
apps/crud/migrations/0004_alter_data_plan_alter_data_user.py
Normal file
26
apps/crud/migrations/0004_alter_data_plan_alter_data_user.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Generated by Django 5.1.4 on 2025-06-10 12:07
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('crud', '0003_alter_plan_owner'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='data',
|
||||
name='plan',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='crud.plan'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='data',
|
||||
name='user',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
||||
|
|
@ -12,9 +12,15 @@ class Plan(models.Model):
|
|||
|
||||
|
||||
class Data(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.PROTECT)
|
||||
plan = models.ForeignKey(Plan, on_delete=models.PROTECT)
|
||||
user = models.ForeignKey(User, on_delete=models.PROTECT, null=True, blank=True)
|
||||
plan = models.ForeignKey(Plan, on_delete=models.PROTECT, null=True, blank=True)
|
||||
data = models.JSONField(default=dict)
|
||||
|
||||
@property
|
||||
def application(self):
|
||||
return str(self.plan.application.uuid)
|
||||
|
||||
class Meta:
|
||||
unique_together = ('user', 'plan')
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@ from .models import Data, Plan
|
|||
class DataSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Data
|
||||
fields = ('user',
|
||||
'data',
|
||||
)
|
||||
fields = (
|
||||
'data',
|
||||
)
|
||||
|
||||
read_only_fields = ['user']
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class CRUDRouter(DefaultRouter):
|
|||
),
|
||||
# Detail route.
|
||||
Route(
|
||||
url=r'^{prefix}/item{trailing_slash}$',
|
||||
url=r'^{prefix}{trailing_slash}$',
|
||||
mapping={
|
||||
'get': 'retrieve',
|
||||
'put': 'update',
|
||||
|
|
@ -40,7 +40,7 @@ class CRUDRouter(DefaultRouter):
|
|||
# Dynamically generated detail routes. Generated using
|
||||
# @action(detail=True) decorator on methods of the viewset.
|
||||
DynamicRoute(
|
||||
url=r'^{prefix}/item/{url_path}{trailing_slash}$',
|
||||
url=r'^{prefix}/{url_path}{trailing_slash}$',
|
||||
name='{basename}-{url_name}',
|
||||
detail=True,
|
||||
initkwargs={}
|
||||
|
|
@ -52,6 +52,7 @@ router = CRUDRouter()
|
|||
|
||||
app_name = 'crud'
|
||||
|
||||
router.register('', views.DataViewSet, basename='crud')
|
||||
router.register('user', views.UserDataViewSet, basename='user-data')
|
||||
router.register('application', views.ApplicationDataViewSet, basename='application-data')
|
||||
|
||||
urlpatterns = router.urls
|
||||
|
|
|
|||
|
|
@ -7,13 +7,15 @@ from rest_framework.viewsets import GenericViewSet
|
|||
|
||||
from apps.crud.models import Data, Plan
|
||||
from apps.crud.serializers import DataSerializer
|
||||
from apps.gooyal_oauth2.models import Application
|
||||
from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements
|
||||
from apps.gooyal_oauth2.utils import get_application
|
||||
|
||||
|
||||
class DataViewSet(mixins.RetrieveModelMixin,
|
||||
mixins.UpdateModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
GenericViewSet):
|
||||
class UserDataViewSet(mixins.RetrieveModelMixin,
|
||||
mixins.UpdateModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
GenericViewSet):
|
||||
queryset = Data.objects.all()
|
||||
serializer_class = DataSerializer
|
||||
|
||||
|
|
@ -28,7 +30,7 @@ class DataViewSet(mixins.RetrieveModelMixin,
|
|||
def get_object(self):
|
||||
user = self.request.user
|
||||
auth = self.request.auth
|
||||
if auth:
|
||||
if auth and user:
|
||||
# plan = Plan.objects.filter(application_id=auth.application_id).first()
|
||||
plan, created = Plan.objects.get_or_create(application_id=auth.application_id)
|
||||
# data, created = Data.objects.get_or_create(plan_id=plan.pk, user=user)
|
||||
|
|
@ -36,3 +38,25 @@ class DataViewSet(mixins.RetrieveModelMixin,
|
|||
return data
|
||||
|
||||
raise Http404
|
||||
|
||||
|
||||
class ApplicationDataViewSet(mixins.RetrieveModelMixin,
|
||||
GenericViewSet):
|
||||
queryset = Application.objects.all()
|
||||
serializer_class = DataSerializer
|
||||
|
||||
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
|
||||
required_alternate_scopes = {
|
||||
"GET": [["data_crud.data:retrieve"]],
|
||||
}
|
||||
|
||||
def get_object(self):
|
||||
application = get_application(self.request)
|
||||
if application:
|
||||
# plan = Plan.objects.filter(application_id=auth.application_id).first()
|
||||
plan, created = Plan.objects.get_or_create(application=application)
|
||||
# data, created = Data.objects.get_or_create(plan_id=plan.pk, user=user)
|
||||
data, created = Data.objects.get_or_create(plan=plan, user=None)
|
||||
return data
|
||||
|
||||
raise Http404
|
||||
|
|
|
|||
6
apps/gooyal_oauth2/utils.py
Normal file
6
apps/gooyal_oauth2/utils.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
def get_application(request):
|
||||
try:
|
||||
application = request.auth.application
|
||||
except:
|
||||
application = None
|
||||
return application
|
||||
|
|
@ -91,7 +91,7 @@ class OAuthCode(models.Model):
|
|||
return code_challenge
|
||||
|
||||
def generate_login_url(self):
|
||||
url = f'''{settings.BASE_OAUTH2_PROVIDER_URL}/oauth2/authorize/?response_type=code&code_challenge={self.generate_code_challenge()}&code_challenge_method=S256&client_id={settings.CLIENT_ID}&scope={settings.SCOPES}&state={self.uuid}'''
|
||||
url = f'''{settings.BASE_OAUTH2_PROVIDER_PUBLIC_URL}/authorize/?response_type=code&code_challenge={self.generate_code_challenge()}&code_challenge_method=S256&client_id={settings.CLIENT_ID}&scope={settings.SCOPES}&state={self.uuid}'''
|
||||
return url
|
||||
|
||||
def validate_code(self):
|
||||
|
|
@ -107,6 +107,6 @@ class OAuthCode(models.Model):
|
|||
"grant_type": "authorization_code"
|
||||
}
|
||||
|
||||
response = requests.post(f'{settings.BASE_OAUTH2_PROVIDER_URL}/oauth2/token/', data=data, headers=headers)
|
||||
response = requests.post(f'{settings.BASE_OAUTH2_PROVIDER_PRIVATE_URL}/token/', data=data, headers=headers)
|
||||
|
||||
return response.json()
|
||||
|
|
@ -76,7 +76,9 @@ OAUTH2_PROVIDER_ID_TOKEN_MODEL = "gooyal_oauth2.IDToken"
|
|||
OAUTH2_PROVIDER_GRANT_MODEL = "gooyal_oauth2.Grant"
|
||||
OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "gooyal_oauth2.RefreshToken"
|
||||
|
||||
BASE_OAUTH2_PROVIDER_URL = "https://accounts.gooyal.com"
|
||||
BASE_OAUTH2_PROVIDER_PUBLIC_URL = config("BASE_OAUTH2_PROVIDER_PUBLIC_URL")
|
||||
BASE_OAUTH2_PROVIDER_PRIVATE_URL = config("BASE_OAUTH2_PROVIDER_PRIVATE_URL")
|
||||
|
||||
CLIENT_ID = config('CLIENT_ID')
|
||||
CLIENT_SECRET = config('CLIENT_SECRET')
|
||||
SCOPES = config('SCOPES', default='')
|
||||
|
|
@ -88,7 +90,7 @@ OAUTH2_PROVIDER = {
|
|||
# 'groups': 'Access to your groups',
|
||||
# 'introspection': 'Introspect token scope'},
|
||||
'OAUTH2_VALIDATOR_CLASS': 'apps.gooyal_oauth2.validators.OAuth2Validator',
|
||||
'RESOURCE_SERVER_INTROSPECTION_URL': BASE_OAUTH2_PROVIDER_URL + '/oauth2/introspect/',
|
||||
'RESOURCE_SERVER_INTROSPECTION_URL': BASE_OAUTH2_PROVIDER_PRIVATE_URL + '/introspect/',
|
||||
# 'RESOURCE_SERVER_AUTH_TOKEN': '3yUqsWtwKYKHnfivFcJu', # OR this but not both:
|
||||
'RESOURCE_SERVER_INTROSPECTION_CREDENTIALS': (CLIENT_ID, CLIENT_SECRET),
|
||||
}
|
||||
|
|
@ -220,6 +222,8 @@ SPECTACULAR_SETTINGS = {
|
|||
'VERSION': '1.0.0',
|
||||
'SERVE_INCLUDE_SCHEMA': False,
|
||||
# OTHER SETTINGS
|
||||
"PARSER_WHITELIST": ["rest_framework.parsers.JSONParser"],
|
||||
|
||||
}
|
||||
|
||||
AUTH_USER_MODEL = 'users.User'
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ urlpatterns = [
|
|||
path('', include('apps.core.urls')),
|
||||
path('users/', include('apps.users.urls')),
|
||||
path('oauth2/', include('oauth2_provider.urls', namespace='oauth2_provider')),
|
||||
path('crud/', include('apps.crud.urls', namespace='crud')),
|
||||
path('data/', include('apps.crud.urls', namespace='crud')),
|
||||
]
|
||||
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
|
|
|||
|
|
@ -42,8 +42,9 @@
|
|||
<div class="row vh-100 overflow-auto">
|
||||
{# {% include "include/sidebar.html" %}#}
|
||||
<div class="col d-flex flex-column h-sm-100">
|
||||
<div class="m-0 p-0 w-100 " style="background-color: yellow; height: 3px;"></div>
|
||||
<main class="row overflow-auto pb-3">
|
||||
<div class="col pt-3">
|
||||
<div class="col pt-2">
|
||||
{% include "include/top_bar.html" %}
|
||||
{% include "include/messages.html" %}
|
||||
{# {% block breadcrumbs %}#}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
{% load crispy_forms_tags %}
|
||||
{% load jalali_tags %}
|
||||
|
||||
{% block title %}wallet: Home{% endblock %}
|
||||
{% block page_title %}wallet: Home{% endblock %}
|
||||
{% block title %}CRUD: Home{% endblock %}
|
||||
{% block page_title %}CRUD: Home{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue