This commit is contained in:
Victor Alexandrovich Tsyrenschikov
2025-09-15 23:54:59 +05:00
parent 41f6699da9
commit 42ed1b04cb
69 changed files with 5614 additions and 0 deletions

0
apps/charts/__init__.py Normal file
View File

3
apps/charts/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
apps/charts/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ChartsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.charts'

View File

3
apps/charts/models.py Normal file
View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
apps/charts/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

7
apps/charts/urls.py Normal file
View File

@@ -0,0 +1,7 @@
from django.urls import path
from apps.charts import views
urlpatterns = [
path("", views.index, name="charts"),
]

13
apps/charts/views.py Normal file
View File

@@ -0,0 +1,13 @@
from django.shortcuts import render
from django.core import serializers
from apps.pages.models import *
# Create your views here.
def index(request):
products = serializers.serialize('json', Product.objects.all())
context = {
'segment': 'charts',
'products': products
}
return render(request, 'charts/index.html', context)

5
apps/dyn_api/__init__.py Normal file
View File

@@ -0,0 +1,5 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""

8
apps/dyn_api/admin.py Normal file
View File

@@ -0,0 +1,8 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""
from django.contrib import admin
# Register your models here.

10
apps/dyn_api/apps.py Normal file
View File

@@ -0,0 +1,10 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""
from django.apps import AppConfig
class DynApiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.dyn_api'

63
apps/dyn_api/helpers.py Normal file
View File

@@ -0,0 +1,63 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""
import datetime, sys, inspect, importlib
from functools import wraps
from django.db import models
from django.http import HttpResponseRedirect, HttpResponse
from rest_framework import serializers
class Utils:
@staticmethod
def get_class(config, name: str) -> models.Model:
return Utils.model_name_to_class(config[name])
@staticmethod
def get_manager(config, name: str) -> models.Manager:
return Utils.get_class(config, name).objects
@staticmethod
def get_serializer(config, name: str):
class Serializer(serializers.ModelSerializer):
class Meta:
model = Utils.get_class(config, name)
fields = '__all__'
return Serializer
@staticmethod
def model_name_to_class(name: str):
model_name = name.split('.')[-1]
model_import = name.replace('.'+model_name, '')
module = importlib.import_module(model_import)
cls = getattr(module, model_name)
return cls
def check_permission(function):
@wraps(function)
def wrap(viewRequest, *args, **kwargs):
try:
# Check user
if viewRequest.request.user.is_authenticated:
return function(viewRequest, *args, **kwargs)
# For authentication for guests
return HttpResponseRedirect('/login/')
except Exception as e:
# On error
return HttpResponse( 'Error: ' + str( e ) )
return wrap

View File

8
apps/dyn_api/tests.py Normal file
View File

@@ -0,0 +1,8 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""
from django.test import TestCase
# Create your tests here.

16
apps/dyn_api/urls.py Normal file
View File

@@ -0,0 +1,16 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""
from django.contrib import admin
from django.urls import path
from apps.dyn_api import views
urlpatterns = [
path('api/', views.index, name="dynamic_api"),
path('api/<str:model_name>/' , views.DynamicAPI.as_view(), name="model_api"),
path('api/<str:model_name>/<str:id>' , views.DynamicAPI.as_view()),
path('api/<str:model_name>/<str:id>/' , views.DynamicAPI.as_view()),
]

155
apps/dyn_api/views.py Normal file
View File

@@ -0,0 +1,155 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""
from django.http import Http404
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.shortcuts import render, redirect, get_object_or_404
from rest_framework.generics import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from django.http import HttpResponse
from django.conf import settings
DYNAMIC_API = {}
try:
DYNAMIC_API = getattr(settings, 'DYNAMIC_API')
except:
pass
from .helpers import Utils
def index(request):
context = {
'routes' : settings.DYNAMIC_API.keys(),
'segment': 'dynamic_api'
}
return render(request, 'dyn_api/index.html', context)
class DynamicAPI(APIView):
# READ : GET api/model/id or api/model
def get(self, request, **kwargs):
model_id = kwargs.get('id', None)
try:
if model_id is not None:
# Validate for integer
try:
model_id = int(model_id)
if model_id < 0:
raise ValueError('Expect positive int')
except ValueError as e:
return Response(data={
'message': 'Input Error = ' + str(e),
'success': False
}, status=400)
thing = get_object_or_404(Utils.get_manager(DYNAMIC_API, kwargs.get('model_name')), id=model_id)
model_serializer = Utils.get_serializer(DYNAMIC_API, kwargs.get('model_name'))(instance=thing)
output = model_serializer.data
else:
all_things = Utils.get_manager(DYNAMIC_API, kwargs.get('model_name')).all()
thing_serializer = Utils.get_serializer(DYNAMIC_API, kwargs.get('model_name'))
output = []
for thing in all_things:
output.append(thing_serializer(instance=thing).data)
except KeyError:
return Response(data={
'message': 'this model is not activated or not exist.',
'success': False
}, status=400)
except Http404:
return Response(data={
'message': 'object with given id not found.',
'success': False
}, status=404)
return Response(data={
'data': output,
'success': True
}, status=200)
# CREATE : POST api/model/
#@check_permission
def post(self, request, **kwargs):
try:
model_serializer = Utils.get_serializer(DYNAMIC_API, kwargs.get('model_name'))(data=request.data)
if model_serializer.is_valid():
model_serializer.save()
else:
return Response(data={
**model_serializer.errors,
'success': False
}, status=400)
except KeyError:
return Response(data={
'message': 'this model is not activated or not exist.',
'success': False
}, status=400)
return Response(data={
'message': 'Record Created.',
'success': True
}, status=200)
# UPDATE : PUT api/model/id/
#@check_permission
def put(self, request, **kwargs):
try:
thing = get_object_or_404(Utils.get_manager(DYNAMIC_API, kwargs.get('model_name')), id=kwargs.get('id'))
model_serializer = Utils.get_serializer(DYNAMIC_API, kwargs.get('model_name'))(instance=thing,
data=request.data,
partial=True)
if model_serializer.is_valid():
model_serializer.save()
else:
return Response(data={
**model_serializer.errors,
'success': False
}, status=400)
except KeyError:
return Response(data={
'message': 'this model is not activated or not exist.',
'success': False
}, status=400)
except Http404:
return Response(data={
'message': 'object with given id not found.',
'success': False
}, status=404)
return Response(data={
'message': 'Record Updated.',
'success': True
}, status=200)
# DELETE : DELETE api/model/id/
#@check_permission
def delete(self, request, **kwargs):
try:
model_manager = Utils.get_manager(DYNAMIC_API, kwargs.get('model_name'))
to_delete_id = kwargs.get('id')
model_manager.get(id=to_delete_id).delete()
except KeyError:
return Response(data={
'message': 'this model is not activated or not exist.',
'success': False
}, status=400)
except Utils.get_class(DYNAMIC_API, kwargs.get('model_name')).DoesNotExist as e:
return Response(data={
'message': 'object with given id not found.',
'success': False
}, status=404)
return Response(data={
'message': 'Record Deleted.',
'success': True
}, status=200)

0
apps/dyn_dt/.gitkeep Normal file
View File

0
apps/dyn_dt/__init__.py Normal file
View File

8
apps/dyn_dt/admin.py Normal file
View File

@@ -0,0 +1,8 @@
from django.contrib import admin
from .models import *
# Register your models here.
admin.site.register(PageItems)
admin.site.register(HideShowFilter)
admin.site.register(ModelFilter)

5
apps/dyn_dt/apps.py Normal file
View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class DynDtConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.dyn_dt'

3
apps/dyn_dt/forms.py Normal file
View File

@@ -0,0 +1,3 @@
from django import forms
# Create your forms here.

View File

@@ -0,0 +1,63 @@
# Generated by Django 4.2.9 on 2025-03-30 11:44
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="HideShowFilter",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("parent", models.CharField(blank=True, max_length=255, null=True)),
("key", models.CharField(max_length=255)),
("value", models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name="ModelFilter",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("parent", models.CharField(blank=True, max_length=255, null=True)),
("key", models.CharField(max_length=255)),
("value", models.CharField(max_length=255)),
],
),
migrations.CreateModel(
name="PageItems",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("parent", models.CharField(blank=True, max_length=255, null=True)),
("items_per_page", models.IntegerField(default=25)),
],
),
]

View File

24
apps/dyn_dt/models.py Normal file
View File

@@ -0,0 +1,24 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
# Create your models here.
class PageItems(models.Model):
parent = models.CharField(max_length=255, null=True, blank=True)
items_per_page = models.IntegerField(default=25)
class HideShowFilter(models.Model):
parent = models.CharField(max_length=255, null=True, blank=True)
key = models.CharField(max_length=255)
value = models.BooleanField(default=False)
def __str__(self):
return self.key
class ModelFilter(models.Model):
parent = models.CharField(max_length=255, null=True, blank=True)
key = models.CharField(max_length=255)
value = models.CharField(max_length=255)
def __str__(self):
return self.key

View File

View File

@@ -0,0 +1,22 @@
from django import template
from datetime import datetime
register = template.Library()
@register.filter(name="getattribute")
def getattribute(value, arg):
try:
attr_value = getattr(value, arg)
if isinstance(attr_value, datetime):
return attr_value.strftime("%Y-%m-%d %H:%M:%S")
return attr_value
except:
return ''
@register.filter
def get(dict_data, key):
return dict_data.get(key, [])

3
apps/dyn_dt/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

18
apps/dyn_dt/urls.py Normal file
View File

@@ -0,0 +1,18 @@
from django.urls import path
from apps.dyn_dt import views
urlpatterns = [
path('dynamic-dt/', views.index, name="dynamic_dt"),
path('create-filter/<str:model_name>/', views.create_filter, name="create_filter"),
path('create-page-items/<str:model_name>/', views.create_page_items, name="create_page_items"),
path('create-hide-show-items/<str:model_name>/', views.create_hide_show_filter, name="create_hide_show_filter"),
path('delete-filter/<str:model_name>/<int:id>/', views.delete_filter, name="delete_filter"),
path('create/<str:aPath>/', views.create, name="create"),
path('delete/<str:aPath>/<int:id>/', views.delete, name="delete"),
path('update/<str:aPath>/<int:id>/', views.update, name="update"),
path('export-csv/<str:aPath>/', views.ExportCSVView.as_view(), name='export_csv'),
path('dynamic-dt/<str:aPath>/', views.model_dt, name="model_dt"),
]

13
apps/dyn_dt/utils.py Normal file
View File

@@ -0,0 +1,13 @@
from django.db.models import Q
def user_filter(request, queryset, fields, fk_fields=[]):
value = request.GET.get('search')
if value:
dynamic_q = Q()
for field in fields:
if field not in fk_fields:
dynamic_q |= Q(**{f'{field}__icontains': value})
return queryset.filter(dynamic_q)
return queryset

315
apps/dyn_dt/views.py Normal file
View File

@@ -0,0 +1,315 @@
import requests, base64, json, csv
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.utils import timezone
from django.http import HttpResponse, JsonResponse
from django.utils.safestring import mark_safe
from django.conf import settings
from django.urls import reverse
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.urls import reverse
from django.views import View
from django.db import models
from pprint import pp
from apps.dyn_dt.models import ModelFilter, PageItems, HideShowFilter
from apps.dyn_dt.utils import user_filter
from cli import *
# Create your views here.
def index(request):
context = {
'routes' : settings.DYNAMIC_DATATB.keys(),
'segment': 'dynamic_dt'
}
return render(request, 'dyn_dt/index.html', context)
def create_filter(request, model_name):
model_name = model_name.lower()
if request.method == "POST":
keys = request.POST.getlist('key')
values = request.POST.getlist('value')
for i in range(len(keys)):
key = keys[i]
value = values[i]
ModelFilter.objects.update_or_create(
parent=model_name,
key=key,
defaults={'value': value}
)
return redirect(reverse('model_dt', args=[model_name]))
def create_page_items(request, model_name):
model_name = model_name.lower()
if request.method == 'POST':
items = request.POST.get('items')
page_items, created = PageItems.objects.update_or_create(
parent=model_name,
defaults={'items_per_page':items}
)
return redirect(reverse('model_dt', args=[model_name]))
def create_hide_show_filter(request, model_name):
model_name = model_name.lower()
if request.method == "POST":
data_str = list(request.POST.keys())[0]
data = json.loads(data_str)
HideShowFilter.objects.update_or_create(
parent=model_name,
key=data.get('key'),
defaults={'value': data.get('value')}
)
response_data = {'message': 'Model updated successfully'}
return JsonResponse(response_data)
return JsonResponse({'error': 'Invalid request'}, status=400)
def delete_filter(request, model_name, id):
model_name = model_name.lower()
filter_instance = ModelFilter.objects.get(id=id, parent=model_name)
filter_instance.delete()
return redirect(reverse('model_dt', args=[model_name]))
def get_model_field_names(model, field_type):
"""Returns a list of field names based on the given field type."""
return [
field.name for field in model._meta.get_fields()
if isinstance(field, field_type)
]
def model_dt(request, aPath):
aModelName = None
aModelClass = None
choices_dict = {}
if aPath in settings.DYNAMIC_DATATB.keys():
aModelName = settings.DYNAMIC_DATATB[aPath]
aModelClass = name_to_class(aModelName)
if not aModelClass:
return HttpResponse( ' > ERR: Getting ModelClass for path: ' + aPath )
#db_fields = [field.name for field in aModelClass._meta.get_fields() if not field.is_relation]
db_fields = [field.name for field in aModelClass._meta.fields]
fk_fields = get_model_fk_values(aModelClass)
db_filters = []
for f in db_fields:
if f not in fk_fields.keys():
db_filters.append( f )
for field in aModelClass._meta.fields:
if field.choices:
choices_dict[field.name] = field.choices
field_names = []
for field_name in db_fields:
fields, created = HideShowFilter.objects.get_or_create(key=field_name, parent=aPath.lower())
if fields.key in db_fields:
field_names.append(fields)
model_series = {}
for f in db_fields:
f_values = list ( aModelClass.objects.values_list( f, flat=True) )
model_series[ f ] = ', '.join( str(i) for i in f_values)
# model filter
filter_string = {}
filter_instance = ModelFilter.objects.filter(parent=aPath.lower())
for filter_data in filter_instance:
if filter_data.key in db_fields:
filter_string[f'{filter_data.key}__icontains'] = filter_data.value
order_by = request.GET.get('order_by', 'id')
if order_by not in db_fields:
order_by = 'id'
queryset = aModelClass.objects.filter(**filter_string).order_by(order_by)
item_list = user_filter(request, queryset, db_fields, fk_fields.keys())
# pagination
page_items = PageItems.objects.filter(parent=aPath.lower()).last()
p_items = 25
if page_items:
p_items = page_items.items_per_page
page = request.GET.get('page', 1)
paginator = Paginator(item_list, p_items)
try:
items = paginator.page(page)
except PageNotAnInteger:
return redirect(reverse('model_dt', args=[aPath]))
except EmptyPage:
return redirect(reverse('model_dt', args=[aPath]))
read_only_fields = ('id', )
integer_fields = get_model_field_names(aModelClass, models.IntegerField)
date_time_fields = get_model_field_names(aModelClass, models.DateTimeField)
email_fields = get_model_field_names(aModelClass, models.EmailField)
text_fields = get_model_field_names(aModelClass, (models.TextField, models.CharField))
context = {
'page_title': 'Dynamic DataTable - ' + aPath.lower().title(),
'link': aPath,
'field_names': field_names,
'db_field_names': db_fields,
'db_filters': db_filters,
'items': items,
'page_items': p_items,
'filter_instance': filter_instance,
'read_only_fields': read_only_fields,
'integer_fields': integer_fields,
'date_time_fields': date_time_fields,
'email_fields': email_fields,
'text_fields': text_fields,
'fk_fields_keys': list( fk_fields.keys() ),
'fk_fields': fk_fields ,
'choices_dict': choices_dict,
'segment': 'dynamic_dt'
}
return render(request, 'dyn_dt/model.html', context)
@login_required(login_url='/accounts/login/')
def create(request, aPath):
aModelClass = None
if aPath in settings.DYNAMIC_DATATB.keys():
aModelName = settings.DYNAMIC_DATATB[aPath]
aModelClass = name_to_class(aModelName)
if not aModelClass:
return HttpResponse( ' > ERR: Getting ModelClass for path: ' + aPath )
if request.method == 'POST':
data = {}
fk_fields = get_model_fk(aModelClass)
for attribute, value in request.POST.items():
if attribute == 'csrfmiddlewaretoken':
continue
# Process FKs
if attribute in fk_fields.keys():
value = name_to_class( fk_fields[attribute] ).objects.filter(id=value).first()
data[attribute] = value if value else ''
aModelClass.objects.create(**data)
return redirect(request.META.get('HTTP_REFERER'))
@login_required(login_url='/accounts/login/')
def delete(request, aPath, id):
aModelClass = None
if aPath in settings.DYNAMIC_DATATB.keys():
aModelName = settings.DYNAMIC_DATATB[aPath]
aModelClass = name_to_class(aModelName)
if not aModelClass:
return HttpResponse( ' > ERR: Getting ModelClass for path: ' + aPath )
item = aModelClass.objects.get(id=id)
item.delete()
return redirect(request.META.get('HTTP_REFERER'))
@login_required(login_url='/accounts/login/')
def update(request, aPath, id):
aModelClass = None
if aPath in settings.DYNAMIC_DATATB.keys():
aModelName = settings.DYNAMIC_DATATB[aPath]
aModelClass = name_to_class(aModelName)
if not aModelClass:
return HttpResponse( ' > ERR: Getting ModelClass for path: ' + aPath )
item = aModelClass.objects.get(id=id)
fk_fields = get_model_fk(aModelClass)
if request.method == 'POST':
for attribute, value in request.POST.items():
if attribute == 'csrfmiddlewaretoken':
continue
if getattr(item, attribute, value) is not None:
# Process FKs
if attribute in fk_fields.keys():
value = name_to_class( fk_fields[attribute] ).objects.filter(id=value).first()
setattr(item, attribute, value)
item.save()
return redirect(request.META.get('HTTP_REFERER'))
# Export as CSV
class ExportCSVView(View):
def get(self, request, aPath):
aModelName = None
aModelClass = None
if aPath in settings.DYNAMIC_DATATB.keys():
aModelName = settings.DYNAMIC_DATATB[aPath]
aModelClass = name_to_class(aModelName)
if not aModelClass:
return HttpResponse( ' > ERR: Getting ModelClass for path: ' + aPath )
db_field_names = [field.name for field in aModelClass._meta.get_fields()]
fields = []
show_fields = HideShowFilter.objects.filter(value=False, parent=aPath.lower())
for field in show_fields:
if field.key in db_field_names:
fields.append(field.key)
else:
print(f"Field {field.key} does not exist in {aModelClass} model.")
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = f'attachment; filename="{aPath.lower()}.csv"'
writer = csv.writer(response)
writer.writerow(fields) # Write the header
filter_string = {}
filter_instance = ModelFilter.objects.filter(parent=aPath.lower())
for filter_data in filter_instance:
filter_string[f'{filter_data.key}__icontains'] = filter_data.value
order_by = request.GET.get('order_by', 'id')
queryset = aModelClass.objects.filter(**filter_string).order_by(order_by)
items = user_filter(request, queryset, db_field_names)
for item in items:
row_data = []
for field in fields:
try:
row_data.append(getattr(item, field))
except AttributeError:
row_data.append('')
writer.writerow(row_data)
return response

0
apps/pages/__init__.py Normal file
View File

3
apps/pages/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
apps/pages/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class PagesConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "apps.pages"

View File

@@ -0,0 +1,23 @@
# Generated by Django 4.2.9 on 2025-04-06 16:19
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Product',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('name', models.CharField(max_length=100)),
('info', models.CharField(default='', max_length=100)),
('price', models.IntegerField(blank=True, null=True)),
],
),
]

View File

12
apps/pages/models.py Normal file
View File

@@ -0,0 +1,12 @@
from django.db import models
# Create your models here.
class Product(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length = 100)
info = models.CharField(max_length = 100, default = '')
price = models.IntegerField(blank=True, null=True)
def __str__(self):
return self.name

3
apps/pages/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

7
apps/pages/urls.py Normal file
View File

@@ -0,0 +1,7 @@
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]

9
apps/pages/views.py Normal file
View File

@@ -0,0 +1,9 @@
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def index(request):
# Page from the theme
return render(request, 'pages/index.html', {'segment': 'dashboard'})