Compare commits
No commits in common. "709ee8c454f37d1ce63609521fb4acee4661a43f" and "f21f156127402144e0496eccb39fbadb8946a388" have entirely different histories.
709ee8c454
...
f21f156127
12 changed files with 23 additions and 98 deletions
|
|
@ -27,12 +27,4 @@ def get_proper_elided_page_range(p, number, on_each_side=2, on_ends=2):
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def get_item(dictionary, key):
|
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>
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
# 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,15 +12,9 @@ class Plan(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Data(models.Model):
|
class Data(models.Model):
|
||||||
user = models.ForeignKey(User, on_delete=models.PROTECT, null=True, blank=True)
|
user = models.ForeignKey(User, on_delete=models.PROTECT)
|
||||||
plan = models.ForeignKey(Plan, on_delete=models.PROTECT, null=True, blank=True)
|
plan = models.ForeignKey(Plan, on_delete=models.PROTECT)
|
||||||
data = models.JSONField(default=dict)
|
data = models.JSONField(default=dict)
|
||||||
|
|
||||||
@property
|
|
||||||
def application(self):
|
|
||||||
return str(self.plan.application.uuid)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('user', 'plan')
|
unique_together = ('user', 'plan')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ from .models import Data, Plan
|
||||||
class DataSerializer(serializers.ModelSerializer):
|
class DataSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Data
|
model = Data
|
||||||
fields = (
|
fields = ('user',
|
||||||
'data',
|
'data',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
read_only_fields = ['user']
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class CRUDRouter(DefaultRouter):
|
||||||
),
|
),
|
||||||
# Detail route.
|
# Detail route.
|
||||||
Route(
|
Route(
|
||||||
url=r'^{prefix}{trailing_slash}$',
|
url=r'^{prefix}/item{trailing_slash}$',
|
||||||
mapping={
|
mapping={
|
||||||
'get': 'retrieve',
|
'get': 'retrieve',
|
||||||
'put': 'update',
|
'put': 'update',
|
||||||
|
|
@ -40,7 +40,7 @@ class CRUDRouter(DefaultRouter):
|
||||||
# Dynamically generated detail routes. Generated using
|
# Dynamically generated detail routes. Generated using
|
||||||
# @action(detail=True) decorator on methods of the viewset.
|
# @action(detail=True) decorator on methods of the viewset.
|
||||||
DynamicRoute(
|
DynamicRoute(
|
||||||
url=r'^{prefix}/{url_path}{trailing_slash}$',
|
url=r'^{prefix}/item/{url_path}{trailing_slash}$',
|
||||||
name='{basename}-{url_name}',
|
name='{basename}-{url_name}',
|
||||||
detail=True,
|
detail=True,
|
||||||
initkwargs={}
|
initkwargs={}
|
||||||
|
|
@ -52,7 +52,6 @@ router = CRUDRouter()
|
||||||
|
|
||||||
app_name = 'crud'
|
app_name = 'crud'
|
||||||
|
|
||||||
router.register('user', views.UserDataViewSet, basename='user-data')
|
router.register('', views.DataViewSet, basename='crud')
|
||||||
router.register('application', views.ApplicationDataViewSet, basename='application-data')
|
|
||||||
|
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,13 @@ from rest_framework.viewsets import GenericViewSet
|
||||||
|
|
||||||
from apps.crud.models import Data, Plan
|
from apps.crud.models import Data, Plan
|
||||||
from apps.crud.serializers import DataSerializer
|
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.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements
|
||||||
from apps.gooyal_oauth2.utils import get_application
|
|
||||||
|
|
||||||
|
|
||||||
class UserDataViewSet(mixins.RetrieveModelMixin,
|
class DataViewSet(mixins.RetrieveModelMixin,
|
||||||
mixins.UpdateModelMixin,
|
mixins.UpdateModelMixin,
|
||||||
mixins.DestroyModelMixin,
|
mixins.DestroyModelMixin,
|
||||||
GenericViewSet):
|
GenericViewSet):
|
||||||
queryset = Data.objects.all()
|
queryset = Data.objects.all()
|
||||||
serializer_class = DataSerializer
|
serializer_class = DataSerializer
|
||||||
|
|
||||||
|
|
@ -30,7 +28,7 @@ class UserDataViewSet(mixins.RetrieveModelMixin,
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
auth = self.request.auth
|
auth = self.request.auth
|
||||||
if auth and user:
|
if auth:
|
||||||
# plan = Plan.objects.filter(application_id=auth.application_id).first()
|
# plan = Plan.objects.filter(application_id=auth.application_id).first()
|
||||||
plan, created = Plan.objects.get_or_create(application_id=auth.application_id)
|
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)
|
# data, created = Data.objects.get_or_create(plan_id=plan.pk, user=user)
|
||||||
|
|
@ -38,25 +36,3 @@ class UserDataViewSet(mixins.RetrieveModelMixin,
|
||||||
return data
|
return data
|
||||||
|
|
||||||
raise Http404
|
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
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
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
|
return code_challenge
|
||||||
|
|
||||||
def generate_login_url(self):
|
def generate_login_url(self):
|
||||||
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}'''
|
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}'''
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def validate_code(self):
|
def validate_code(self):
|
||||||
|
|
@ -107,6 +107,6 @@ class OAuthCode(models.Model):
|
||||||
"grant_type": "authorization_code"
|
"grant_type": "authorization_code"
|
||||||
}
|
}
|
||||||
|
|
||||||
response = requests.post(f'{settings.BASE_OAUTH2_PROVIDER_PRIVATE_URL}/token/', data=data, headers=headers)
|
response = requests.post(f'{settings.BASE_OAUTH2_PROVIDER_URL}/oauth2/token/', data=data, headers=headers)
|
||||||
|
|
||||||
return response.json()
|
return response.json()
|
||||||
|
|
@ -76,9 +76,7 @@ OAUTH2_PROVIDER_ID_TOKEN_MODEL = "gooyal_oauth2.IDToken"
|
||||||
OAUTH2_PROVIDER_GRANT_MODEL = "gooyal_oauth2.Grant"
|
OAUTH2_PROVIDER_GRANT_MODEL = "gooyal_oauth2.Grant"
|
||||||
OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "gooyal_oauth2.RefreshToken"
|
OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "gooyal_oauth2.RefreshToken"
|
||||||
|
|
||||||
BASE_OAUTH2_PROVIDER_PUBLIC_URL = config("BASE_OAUTH2_PROVIDER_PUBLIC_URL")
|
BASE_OAUTH2_PROVIDER_URL = "https://accounts.gooyal.com"
|
||||||
BASE_OAUTH2_PROVIDER_PRIVATE_URL = config("BASE_OAUTH2_PROVIDER_PRIVATE_URL")
|
|
||||||
|
|
||||||
CLIENT_ID = config('CLIENT_ID')
|
CLIENT_ID = config('CLIENT_ID')
|
||||||
CLIENT_SECRET = config('CLIENT_SECRET')
|
CLIENT_SECRET = config('CLIENT_SECRET')
|
||||||
SCOPES = config('SCOPES', default='')
|
SCOPES = config('SCOPES', default='')
|
||||||
|
|
@ -90,7 +88,7 @@ OAUTH2_PROVIDER = {
|
||||||
# 'groups': 'Access to your groups',
|
# 'groups': 'Access to your groups',
|
||||||
# 'introspection': 'Introspect token scope'},
|
# 'introspection': 'Introspect token scope'},
|
||||||
'OAUTH2_VALIDATOR_CLASS': 'apps.gooyal_oauth2.validators.OAuth2Validator',
|
'OAUTH2_VALIDATOR_CLASS': 'apps.gooyal_oauth2.validators.OAuth2Validator',
|
||||||
'RESOURCE_SERVER_INTROSPECTION_URL': BASE_OAUTH2_PROVIDER_PRIVATE_URL + '/introspect/',
|
'RESOURCE_SERVER_INTROSPECTION_URL': BASE_OAUTH2_PROVIDER_URL + '/oauth2/introspect/',
|
||||||
# 'RESOURCE_SERVER_AUTH_TOKEN': '3yUqsWtwKYKHnfivFcJu', # OR this but not both:
|
# 'RESOURCE_SERVER_AUTH_TOKEN': '3yUqsWtwKYKHnfivFcJu', # OR this but not both:
|
||||||
'RESOURCE_SERVER_INTROSPECTION_CREDENTIALS': (CLIENT_ID, CLIENT_SECRET),
|
'RESOURCE_SERVER_INTROSPECTION_CREDENTIALS': (CLIENT_ID, CLIENT_SECRET),
|
||||||
}
|
}
|
||||||
|
|
@ -222,8 +220,6 @@ SPECTACULAR_SETTINGS = {
|
||||||
'VERSION': '1.0.0',
|
'VERSION': '1.0.0',
|
||||||
'SERVE_INCLUDE_SCHEMA': False,
|
'SERVE_INCLUDE_SCHEMA': False,
|
||||||
# OTHER SETTINGS
|
# OTHER SETTINGS
|
||||||
"PARSER_WHITELIST": ["rest_framework.parsers.JSONParser"],
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AUTH_USER_MODEL = 'users.User'
|
AUTH_USER_MODEL = 'users.User'
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ urlpatterns = [
|
||||||
path('', include('apps.core.urls')),
|
path('', include('apps.core.urls')),
|
||||||
path('users/', include('apps.users.urls')),
|
path('users/', include('apps.users.urls')),
|
||||||
path('oauth2/', include('oauth2_provider.urls', namespace='oauth2_provider')),
|
path('oauth2/', include('oauth2_provider.urls', namespace='oauth2_provider')),
|
||||||
path('data/', include('apps.crud.urls', namespace='crud')),
|
path('crud/', include('apps.crud.urls', namespace='crud')),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,8 @@
|
||||||
<div class="row vh-100 overflow-auto">
|
<div class="row vh-100 overflow-auto">
|
||||||
{# {% include "include/sidebar.html" %}#}
|
{# {% include "include/sidebar.html" %}#}
|
||||||
<div class="col d-flex flex-column h-sm-100">
|
<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">
|
<main class="row overflow-auto pb-3">
|
||||||
<div class="col pt-2">
|
<div class="col pt-3">
|
||||||
{% include "include/top_bar.html" %}
|
{% include "include/top_bar.html" %}
|
||||||
{% include "include/messages.html" %}
|
{% include "include/messages.html" %}
|
||||||
{# {% block breadcrumbs %}#}
|
{# {% block breadcrumbs %}#}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
{% load crispy_forms_tags %}
|
{% load crispy_forms_tags %}
|
||||||
{% load jalali_tags %}
|
{% load jalali_tags %}
|
||||||
|
|
||||||
{% block title %}CRUD: Home{% endblock %}
|
{% block title %}wallet: Home{% endblock %}
|
||||||
{% block page_title %}CRUD: Home{% endblock %}
|
{% block page_title %}wallet: Home{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue