Доработка API

This commit is contained in:
2026-06-15 10:08:59 +03:00
parent 90b7d1c22f
commit 1ca5e2c32e
3 changed files with 145 additions and 20 deletions

View File

@@ -5,7 +5,7 @@ RETURNS TABLE(confirm boolean)
AS $$
BEGIN
UPDATE nn_db.application
SET nn_db.application.fk_status = in_fk_status
SET fk_status = in_fk_status
WHERE
nn_db.application.pk_application = in_pk_application;

View File

@@ -7,9 +7,10 @@ from django.core.signing import TimestampSigner
from django.utils.encoding import force_str, force_bytes
from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
from django.contrib.auth.hashers import PBKDF2PasswordHasher
from django.http.request import QueryDict
from django.views.decorators.csrf import csrf_exempt
import re
import re, json
def call_func(name: str, parameters: dict):
@@ -31,41 +32,57 @@ def call_func(name: str, parameters: dict):
return [dict(zip(columns, row)) for row in cursor.fetchall()]
def api_get_xcsrf(request):
data = { 'X-CSFRToken': get_token(request) }
data = { 'X-CSRFToken': get_token(request) }
return JsonResponse(data, safe=False, status=200)
# Проверка авторизации
def is_auth(request):
# Проверяем наличие заголовка авторизации
if not request.headers.get('Authorization'):
# Если его нет - возвращаем "Ложь"
return False
# Если токен есть - получаем его, разбивая строку по пробелу, при этом извлекая только последнюю строку
token = request.headers.get('Authorization').split(' ')[-1]
# Инициализируем подписывающий объект
signer = TimestampSigner(salt='django.core.signing')
try:
# Пробуем извлечь из токена имя пользователя
login = signer.unsign(force_str(urlsafe_base64_decode(token)), max_age=1000)
print(login)
except:
# Если не получилось - возвращаем "Ложь"
return False
else:
# Если получилось - запрашиваем в базе данных информацию о пользователе
user = call_func('nn_db.user_get', {'search_value': login})[0]
# И возвращаем полученные данные
return user
# Обработка заявок
def api_applications(request):
# Проверяем, авторизован ли пользователь
user = is_auth(request)
if not user:
# Если пользователь не авторизован - возвращаем ответ с соответствующим сообщением
return JsonResponse({"code": 403, "message":"Доступ запрещен" }, safe=False, status=403)
# Если пользователь авторизован, то проверяем, какой тип запроса был отправлен
if request.method == 'GET':
params = {}
if user['is_admin']:
params['pk_user'] = None
# Если запрос на получение данных
params = {} # Инициализируем объект, хранящий параметры запроса
if user['is_admin']: # Если пользователь - администратор
params['pk_user'] = None # Заполняем параметр null-значением (в Python - None)
else:
params['pk_user'] = str(user['pk_user'])
# params['pk_user'] = None
params['pk_user'] = str(user['pk_user']) # Иначе заполняем параметр идентификатором пользователя
# Запрашиваем данные по заявкам из базы данных по указанному пользователю
results = call_func('nn_db.application_get', params)
# Определяем списки для возвращаемых заявок и ключей, заявки по которым уже есть в первом списке
applications = []
keys = []
for result in results:
if result['pk_application'] not in keys:
application = {}
for result in results: # Проходимся по каждой строке результата из БД
if result['pk_application'] not in keys: # Если ключ текущей заявки не находится в списке ключей
application = {} # Инициализируем словарь и заполняем его данными из ответа БД
application['pk_application'] = result['pk_application']
application['car_number'] = result['car_number']
application['description'] = result['description']
@@ -73,33 +90,140 @@ def api_applications(request):
application['title'] = result['title']
application['fk_user'] = result['fk_user']
application['username'] = result['username']
# Инициализируем список хранения данных об изображениях к заявке
application['images'] = []
# Если есть данные о изображении
if result['pk_image'] is not None:
image = {}
image = {} # Инициализируем словарь картинки и заполняем его данными из ответа БД
image['pk_image'] = result['pk_image']
image['file_name'] = result['file_name']
image['file_ext'] = result['file_ext']
# Добавляем словарь картинки в список картинок заявки
application['images'].append(image)
# Добавляем словарь заявки в список заявок
applications.append(application)
# Добавляем ключ заявки в список обработанных ключей
keys.append(application['pk_application'])
else:
for application in applications:
if application['pk_application'] == result['pk_application']:
image = {}
# Если заявка уже обработана, т.е. эта строка обозначает следующее изображение
for application in applications: # Проходимся по каждой заявке и ищем текущую заявку
if application['pk_application'] == result['pk_application']: # Если ключи совпадают
image = {} # Инициализируем словарь картинки и заполняем его данными из ответа БД
image['pk_image'] = result['pk_image']
image['file_name'] = result['file_name']
image['file_ext'] = result['file_ext']
# Добавляем словарь картинки в список картинок заявки
application['images'].append(image)
# Возвращаем данные пользователю
return JsonResponse({"code": 200, "data": applications}, safe=False, status=200)
# Если запрос на добавление данных
if request.method == 'POST':
params = {}
params = {} # Инициализируем список параметров для запроса и заполняем его данными из POST-запроса
params['car_number'] = request.POST.get('car_number')
params['description'] = request.POST.get('description')
params['fk_user'] = user['pk_user']
params['fk_user'] = str(user['pk_user'])
# Инициализируем список ошибок валидации данных
errors = []
# Автоматическая проверка параметров на заполненность
for key in params:
if params[key] is None or params[key] == '':
errors.append({ key: "Параметр обязателен для заполнения" })
# Если ошибки есть - то возвращаем ответ с ошибкой
if errors:
return JsonResponse({"code": 409, "errors": errors}, safe=False, status=409)
# Проверяем введенные данные на валидность
if re.match(r'^[YETOPAHKXCBMУЕТОРАНКХСВМ]\d{3}[YETOPAHKXCBMУЕТОРАНКХСВМ]{2}\d{1,3}$', params['car_number']) is None:
errors.append({ "car_number": "Государственный номер автомобиля имеет некорректный формат" })
if len(params['description']) < 15:
errors.append({ "description": "Описание нарушения должно быть длинее 5 символов" })
# Если ошибки есть - то возвращаем ответ с ошибкой
if errors:
return JsonResponse({"code": 409, "errors": errors}, safe=False, status=409)
# Отправляем запрос на добавление данных
confirm = call_func('nn_db.application_post', params)
return JsonResponse({"code": 201, "data": confirm}, safe=False, status=201)
# Проверяем, пришел ли в ответ на запрос первичный ключ добавленной заявки
if any(confirm) and 'pk_application' in confirm[0]:
# Если пришёл - то возвращаем данные
return JsonResponse({"code": 201, "data": confirm}, safe=False, status=201)
else:
# Если нет - то возвращаем ошибку с сообщением
return JsonResponse({"code": 520, "message": "Не удалось добавить запись в БД, попробуйте позднее"}, safe=False, status=520)
# Если запрос на изменение заявки:
if request.method == 'PATCH':
if not user['is_admin']: # Если пользователь - не администратор
return JsonResponse({"code": 403, "message":"Доступ запрещен" }, safe=False, status=403) # По ТЗ смена статуса - только у администратора
# Запросы PUT, PATCH и DELETE не работают с Form-data нативно, по этому в теле запроса должен быть JSON объект
data = json.loads(request.body)
params = {} # Инициализируем список параметров для запроса и заполняем его данными из JSON объекта
params['pk_application'] = str(data.get('pk_application'))
params['fk_status'] = str(data.get('fk_status'))
# Инициализируем список ошибок валидации данных
errors = []
# Автоматическая проверка параметров на заполненность
for key in params:
if params[key] is None or params[key] == '':
errors.append({ key: "Параметр обязателен для заполнения" })
# Если ошибки есть - то возвращаем ответ с ошибкой
if errors:
return JsonResponse({"code": 409, "errors": errors}, safe=False, status=409)
confirm = call_func('nn_db.application_patch', params)
# Если ответ не пустой и есть поле подтверждения
if any(confirm) and 'confirm' in confirm[0]:
if confirm[0]['confirm']:
# Если запись обновлена - возвращаем 202 ответ с сообщением
return JsonResponse({"code": 202, "message": "Данные обновлены"}, safe=False, status=202)
else:
# Если запись не обновлена - то возвращаем ответ 304 с сообщением
return JsonResponse({"code": 304, "message": "Данные не обновлены"}, safe=False, status=304)
else:
# Если нет - то возвращаем ошибку с сообщением
return JsonResponse({"code": 520, "message": "Не удалось обновить запись в БД, попробуйте позднее"}, safe=False, status=520)
def api_statuses(request):
user = is_auth(request)
if not user:
return JsonResponse({"code": 403, "message":"Доступ запрещен" }, safe=False, status=403)
if request.method == 'GET':
results = call_func('nn_db.status_get', {})
return JsonResponse({"code": 200, "data": results}, safe=False, status=200)
if request.method == 'POST':
params = {}
params['title'] = request.POST.get('title')
if params['title'] is None:
return JsonResponse({"code": 409, "errors": [{"title": "Параметр обязателен для заполнения"}]}, safe=False, status=409)
if len(params['title']) <= 4:
return JsonResponse({"code": 409, "errors": [{"title": "Описание статуса должно быть длинее 4 символов"}]}, safe=False, status=409)
confirm = call_func('nn_db.status_post', params)
# Проверяем, пришел ли в ответ на запрос первичный ключ добавленной заявки
if any(confirm) and 'pk_status' in confirm[0]:
# Если пришёл - то возвращаем данные
return JsonResponse({"code": 201, "data": confirm}, safe=False, status=201)
else:
# Если нет - то возвращаем ошибку с сообщением
return JsonResponse({"code": 520, "message": "Не удалось добавить запись в БД, попробуйте позднее"}, safe=False, status=520)
def api_users(request):
if is_auth(request):

View File

@@ -20,6 +20,7 @@ from api import views
urlpatterns = [
path('api/applications/', views.api_applications)
, path('api/statuses/', views.api_statuses)
, path('api/get_xcsrf/', views.api_get_xcsrf)
, path('api/users/', views.api_users)
, path('api/auth/', views.api_auth)