barebone app with django and react, sse, jwt token, comport reader, test comport writer, requires com0com, users with groups, sample table vehicles, tokens for access and refresh
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# Django API app
|
||||
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class ApiConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'api'
|
||||
@@ -0,0 +1,34 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from api.models import User
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Creates predefined admin user (username: admin, password: admin)'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
username = 'admin'
|
||||
password = 'admin'
|
||||
|
||||
if User.objects.filter(username=username).exists():
|
||||
self.stdout.write(
|
||||
self.style.WARNING(f'User "{username}" already exists')
|
||||
)
|
||||
return
|
||||
|
||||
user = User.objects.create(
|
||||
username=username,
|
||||
email='admin@scalesapp.com',
|
||||
role='employee',
|
||||
is_admin=True,
|
||||
is_staff=True,
|
||||
is_superuser=True,
|
||||
is_active=True
|
||||
)
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
f'Successfully created admin user: {username} / {password}'
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,174 @@
|
||||
# Generated by Django 4.2 on 2026-01-12 18:39
|
||||
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("auth", "0012_alter_user_first_name_max_length"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="User",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("password", models.CharField(max_length=128, verbose_name="password")),
|
||||
(
|
||||
"last_login",
|
||||
models.DateTimeField(
|
||||
blank=True, null=True, verbose_name="last login"
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_superuser",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Designates that this user has all permissions without explicitly assigning them.",
|
||||
verbose_name="superuser status",
|
||||
),
|
||||
),
|
||||
(
|
||||
"username",
|
||||
models.CharField(
|
||||
error_messages={
|
||||
"unique": "A user with that username already exists."
|
||||
},
|
||||
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
|
||||
max_length=150,
|
||||
unique=True,
|
||||
validators=[
|
||||
django.contrib.auth.validators.UnicodeUsernameValidator()
|
||||
],
|
||||
verbose_name="username",
|
||||
),
|
||||
),
|
||||
(
|
||||
"first_name",
|
||||
models.CharField(
|
||||
blank=True, max_length=150, verbose_name="first name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"last_name",
|
||||
models.CharField(
|
||||
blank=True, max_length=150, verbose_name="last name"
|
||||
),
|
||||
),
|
||||
(
|
||||
"email",
|
||||
models.EmailField(
|
||||
blank=True, max_length=254, verbose_name="email address"
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_staff",
|
||||
models.BooleanField(
|
||||
default=False,
|
||||
help_text="Designates whether the user can log into this admin site.",
|
||||
verbose_name="staff status",
|
||||
),
|
||||
),
|
||||
(
|
||||
"is_active",
|
||||
models.BooleanField(
|
||||
default=True,
|
||||
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
|
||||
verbose_name="active",
|
||||
),
|
||||
),
|
||||
(
|
||||
"date_joined",
|
||||
models.DateTimeField(
|
||||
default=django.utils.timezone.now, verbose_name="date joined"
|
||||
),
|
||||
),
|
||||
(
|
||||
"role",
|
||||
models.CharField(
|
||||
choices=[("employee", "Employee"), ("viewer", "Viewer")],
|
||||
default="viewer",
|
||||
max_length=20,
|
||||
),
|
||||
),
|
||||
("is_admin", models.BooleanField(default=False)),
|
||||
],
|
||||
options={
|
||||
"db_table": "api_user",
|
||||
},
|
||||
managers=[
|
||||
("objects", django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="ComPortReading",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("port", models.CharField(max_length=20)),
|
||||
("data", models.TextField()),
|
||||
("timestamp", models.DateTimeField(auto_now_add=True)),
|
||||
("source_ip", models.GenericIPAddressField(blank=True, null=True)),
|
||||
],
|
||||
options={
|
||||
"ordering": ["-timestamp"],
|
||||
},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="comportreading",
|
||||
index=models.Index(
|
||||
fields=["-timestamp"], name="api_comport_timesta_c2b399_idx"
|
||||
),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="comportreading",
|
||||
index=models.Index(
|
||||
fields=["port", "-timestamp"], name="api_comport_port_123b9f_idx"
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="user",
|
||||
name="groups",
|
||||
field=models.ManyToManyField(
|
||||
blank=True,
|
||||
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
|
||||
related_name="user_set",
|
||||
related_query_name="user",
|
||||
to="auth.group",
|
||||
verbose_name="groups",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="user",
|
||||
name="user_permissions",
|
||||
field=models.ManyToManyField(
|
||||
blank=True,
|
||||
help_text="Specific permissions for this user.",
|
||||
related_name="user_set",
|
||||
related_query_name="user",
|
||||
to="auth.permission",
|
||||
verbose_name="user permissions",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,38 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
"""Custom User model with role and admin flag"""
|
||||
ROLE_CHOICES = [
|
||||
('employee', 'Employee'),
|
||||
('viewer', 'Viewer'),
|
||||
]
|
||||
|
||||
role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='viewer')
|
||||
is_admin = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
db_table = 'api_user'
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.username} ({self.get_role_display()})"
|
||||
|
||||
|
||||
class ComPortReading(models.Model):
|
||||
"""Model to store serial port readings"""
|
||||
port = models.CharField(max_length=20)
|
||||
data = models.TextField()
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
source_ip = models.GenericIPAddressField(null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-timestamp']
|
||||
indexes = [
|
||||
models.Index(fields=['-timestamp']),
|
||||
models.Index(fields=['port', '-timestamp']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.port} - {self.timestamp}"
|
||||
@@ -0,0 +1,194 @@
|
||||
from rest_framework import serializers
|
||||
from django.contrib.auth.password_validation import validate_password
|
||||
from .models import ComPortReading, User
|
||||
from vehicles.models import Vehicle, VehicleExtra
|
||||
from nomenclatures.models import Nomenclature, NomenclatureField
|
||||
|
||||
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
password = serializers.CharField(write_only=True, required=False)
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['id', 'username', 'email', 'first_name', 'last_name',
|
||||
'role', 'is_admin', 'is_active', 'date_joined', 'password']
|
||||
read_only_fields = ['id', 'date_joined']
|
||||
extra_kwargs = {
|
||||
'password': {'write_only': True}
|
||||
}
|
||||
|
||||
def create(self, validated_data):
|
||||
password = validated_data.pop('password', None)
|
||||
user = User(**validated_data)
|
||||
if password:
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
return user
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
password = validated_data.pop('password', None)
|
||||
for attr, value in validated_data.items():
|
||||
setattr(instance, attr, value)
|
||||
if password:
|
||||
instance.set_password(password)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
|
||||
class UserDetailSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for current user details (excludes password)"""
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['id', 'username', 'email', 'first_name', 'last_name',
|
||||
'role', 'is_admin', 'is_active', 'date_joined']
|
||||
read_only_fields = ['id', 'date_joined']
|
||||
|
||||
|
||||
class ChangePasswordSerializer(serializers.Serializer):
|
||||
"""Serializer for password change endpoint"""
|
||||
old_password = serializers.CharField(required=True, write_only=True)
|
||||
new_password = serializers.CharField(required=True, write_only=True)
|
||||
|
||||
def validate_old_password(self, value):
|
||||
user = self.context['request'].user
|
||||
if not user.check_password(value):
|
||||
raise serializers.ValidationError("Old password is incorrect")
|
||||
return value
|
||||
|
||||
def validate_new_password(self, value):
|
||||
# Use Django's password validators
|
||||
validate_password(value, self.context['request'].user)
|
||||
return value
|
||||
|
||||
def save(self, **kwargs):
|
||||
user = self.context['request'].user
|
||||
user.set_password(self.validated_data['new_password'])
|
||||
user.save()
|
||||
return user
|
||||
|
||||
|
||||
class ComPortReadingSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = ComPortReading
|
||||
fields = ['id', 'port', 'data', 'timestamp', 'source_ip']
|
||||
read_only_fields = ['id', 'timestamp']
|
||||
|
||||
|
||||
class VehicleExtraSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = VehicleExtra
|
||||
fields = ['id', 'data']
|
||||
read_only_fields = ['id']
|
||||
|
||||
|
||||
class VehicleSerializer(serializers.ModelSerializer):
|
||||
extra = VehicleExtraSerializer(required=False, allow_null=True)
|
||||
|
||||
class Meta:
|
||||
model = Vehicle
|
||||
fields = ['id', 'vehicle_number', 'extra']
|
||||
read_only_fields = ['id']
|
||||
|
||||
def create(self, validated_data):
|
||||
extra_data = validated_data.pop('extra', None)
|
||||
vehicle = Vehicle.objects.create(**validated_data)
|
||||
|
||||
if extra_data:
|
||||
VehicleExtra.objects.create(vehicle=vehicle, **extra_data)
|
||||
|
||||
return vehicle
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
extra_data = validated_data.pop('extra', None)
|
||||
|
||||
# Update Vehicle fields
|
||||
instance.vehicle_number = validated_data.get('vehicle_number', instance.vehicle_number)
|
||||
instance.save()
|
||||
|
||||
# Handle VehicleExtra update/creation
|
||||
if extra_data is not None:
|
||||
if hasattr(instance, 'extra'):
|
||||
# Update existing VehicleExtra
|
||||
for attr, value in extra_data.items():
|
||||
setattr(instance.extra, attr, value)
|
||||
instance.extra.save()
|
||||
else:
|
||||
# Create new VehicleExtra
|
||||
VehicleExtra.objects.create(vehicle=instance, **extra_data)
|
||||
|
||||
return instance
|
||||
|
||||
def validate_vehicle_number(self, value):
|
||||
if not value or not value.strip():
|
||||
raise serializers.ValidationError("Vehicle number cannot be empty")
|
||||
return value.strip()
|
||||
|
||||
|
||||
class NomenclatureFieldSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = NomenclatureField
|
||||
fields = ['id', 'key', 'field_type']
|
||||
read_only_fields = ['id']
|
||||
|
||||
def validate_key(self, value):
|
||||
if not value or not value.strip():
|
||||
raise serializers.ValidationError("Field key cannot be empty")
|
||||
return value.strip()
|
||||
|
||||
def validate_field_type(self, value):
|
||||
valid_types = [choice[0] for choice in NomenclatureField.FIELD_TYPES]
|
||||
if value not in valid_types:
|
||||
raise serializers.ValidationError(
|
||||
f"Invalid field type. Must be one of: {', '.join(valid_types)}"
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
class NomenclatureSerializer(serializers.ModelSerializer):
|
||||
fields = NomenclatureFieldSerializer(many=True, required=False, source='nomenclaturefield_set')
|
||||
|
||||
class Meta:
|
||||
model = Nomenclature
|
||||
fields = ['id', 'code', 'name', 'applies_to', 'fields']
|
||||
read_only_fields = ['id']
|
||||
|
||||
def create(self, validated_data):
|
||||
fields_data = validated_data.pop('nomenclaturefield_set', [])
|
||||
nomenclature = Nomenclature.objects.create(**validated_data)
|
||||
|
||||
for field_data in fields_data:
|
||||
NomenclatureField.objects.create(nomenclature=nomenclature, **field_data)
|
||||
|
||||
return nomenclature
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
fields_data = validated_data.pop('nomenclaturefield_set', None)
|
||||
|
||||
# Update Nomenclature fields
|
||||
instance.code = validated_data.get('code', instance.code)
|
||||
instance.name = validated_data.get('name', instance.name)
|
||||
instance.applies_to = validated_data.get('applies_to', instance.applies_to)
|
||||
instance.save()
|
||||
|
||||
# Handle fields update - replace all fields
|
||||
if fields_data is not None:
|
||||
# Delete existing fields
|
||||
instance.nomenclaturefield_set.all().delete()
|
||||
# Create new fields
|
||||
for field_data in fields_data:
|
||||
NomenclatureField.objects.create(nomenclature=instance, **field_data)
|
||||
|
||||
return instance
|
||||
|
||||
def validate_code(self, value):
|
||||
if not value or not value.strip():
|
||||
raise serializers.ValidationError("Code cannot be empty")
|
||||
return value.strip()
|
||||
|
||||
def validate_applies_to(self, value):
|
||||
valid_choices = ['vehicle', 'container']
|
||||
if value not in valid_choices:
|
||||
raise serializers.ValidationError(
|
||||
f"Invalid applies_to value. Must be one of: {', '.join(valid_choices)}"
|
||||
)
|
||||
return value
|
||||
@@ -0,0 +1,22 @@
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from rest_framework_simplejwt.views import (
|
||||
TokenObtainPairView,
|
||||
TokenRefreshView,
|
||||
)
|
||||
from . import views
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register(r'users', views.UserViewSet, basename='user')
|
||||
router.register(r'readings', views.ComPortReadingViewSet, basename='reading')
|
||||
router.register(r'vehicles', views.VehicleViewSet, basename='vehicle')
|
||||
router.register(r'nomenclatures', views.NomenclatureViewSet, basename='nomenclature')
|
||||
|
||||
urlpatterns = [
|
||||
# JWT token endpoints
|
||||
path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
|
||||
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
|
||||
|
||||
# Router URLs
|
||||
path('', include(router.urls)),
|
||||
]
|
||||
@@ -0,0 +1,211 @@
|
||||
from rest_framework import viewsets, status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||||
from .models import ComPortReading, User
|
||||
from .serializers import ComPortReadingSerializer, UserSerializer, UserDetailSerializer, ChangePasswordSerializer, VehicleSerializer, NomenclatureSerializer
|
||||
from vehicles.models import Vehicle
|
||||
from nomenclatures.models import Nomenclature
|
||||
|
||||
|
||||
class UserViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
API endpoint for user management.
|
||||
|
||||
list: Get all users
|
||||
create: Create a new user
|
||||
retrieve: Get a specific user
|
||||
update: Update a user
|
||||
destroy: Delete a user
|
||||
me: Get current authenticated user
|
||||
change_password: Change password for current user
|
||||
"""
|
||||
queryset = User.objects.all()
|
||||
serializer_class = UserSerializer
|
||||
filterset_fields = ['role', 'is_admin', 'is_active']
|
||||
ordering = ['username']
|
||||
|
||||
@action(detail=False, methods=['get'], permission_classes=[IsAuthenticated])
|
||||
def me(self, request):
|
||||
"""Get current authenticated user details"""
|
||||
serializer = UserDetailSerializer(request.user)
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(detail=False, methods=['post'], permission_classes=[IsAuthenticated],
|
||||
url_path='change-password', url_name='change_password')
|
||||
def change_password(self, request):
|
||||
"""Change password for current user"""
|
||||
serializer = ChangePasswordSerializer(
|
||||
data=request.data,
|
||||
context={'request': request}
|
||||
)
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
return Response({
|
||||
'detail': 'Password updated successfully'
|
||||
}, status=status.HTTP_200_OK)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class ComPortReadingViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
API endpoint for COM port readings.
|
||||
|
||||
list: Get all readings with optional filtering
|
||||
create: Create a new reading (allows unauthenticated for serial bridge)
|
||||
retrieve: Get a specific reading
|
||||
destroy: Delete a reading
|
||||
latest: Get the latest reading
|
||||
by_port: Get readings for a specific port
|
||||
"""
|
||||
queryset = ComPortReading.objects.all()
|
||||
serializer_class = ComPortReadingSerializer
|
||||
filterset_fields = ['port']
|
||||
ordering = ['-timestamp']
|
||||
|
||||
def get_permissions(self):
|
||||
"""Allow unauthenticated POST for serial bridge"""
|
||||
if self.action == 'create':
|
||||
return [AllowAny()]
|
||||
return [IsAuthenticated()]
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def latest(self, request):
|
||||
"""Get the latest reading"""
|
||||
reading = ComPortReading.objects.first()
|
||||
if reading:
|
||||
serializer = self.get_serializer(reading)
|
||||
return Response(serializer.data)
|
||||
return Response({'detail': 'No readings yet'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def by_port(self, request):
|
||||
"""Get readings for a specific port"""
|
||||
port = request.query_params.get('port', None)
|
||||
if not port:
|
||||
return Response(
|
||||
{'detail': 'port parameter is required'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
readings = ComPortReading.objects.filter(port=port)
|
||||
serializer = self.get_serializer(readings, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
def get_client_ip(self, request):
|
||||
"""Get client IP address"""
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
if x_forwarded_for:
|
||||
ip = x_forwarded_for.split(',')[0]
|
||||
else:
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
return ip
|
||||
|
||||
def perform_create(self, serializer):
|
||||
"""Save the reading with client IP"""
|
||||
serializer.save(source_ip=self.get_client_ip(self.request))
|
||||
|
||||
|
||||
class VehicleViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
API endpoint for vehicle management.
|
||||
|
||||
list: Get all vehicles with pagination
|
||||
create: Create a new vehicle with optional extra data
|
||||
retrieve: Get a specific vehicle by ID
|
||||
update: Full update of vehicle and extra data
|
||||
partial_update: Partial update of vehicle
|
||||
destroy: Delete a vehicle (cascades to VehicleExtra)
|
||||
by_number: Get vehicle by vehicle_number
|
||||
"""
|
||||
queryset = Vehicle.objects.select_related('extra').all()
|
||||
serializer_class = VehicleSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
filterset_fields = ['vehicle_number']
|
||||
search_fields = ['vehicle_number']
|
||||
ordering = ['vehicle_number']
|
||||
|
||||
@action(detail=False, methods=['get'], url_path='by-number')
|
||||
def by_number(self, request):
|
||||
"""Get vehicle by vehicle_number query parameter"""
|
||||
vehicle_number = request.query_params.get('vehicle_number', None)
|
||||
if not vehicle_number:
|
||||
return Response(
|
||||
{'detail': 'vehicle_number parameter is required'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
try:
|
||||
vehicle = Vehicle.objects.get(vehicle_number=vehicle_number)
|
||||
serializer = self.get_serializer(vehicle)
|
||||
return Response(serializer.data)
|
||||
except Vehicle.DoesNotExist:
|
||||
return Response(
|
||||
{'detail': 'Vehicle not found'},
|
||||
status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
|
||||
|
||||
class NomenclatureViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
API endpoint for nomenclature management.
|
||||
|
||||
list: Get all nomenclatures with pagination
|
||||
create: Create a new nomenclature with fields
|
||||
retrieve: Get a specific nomenclature by ID
|
||||
update: Full update of nomenclature and fields
|
||||
partial_update: Partial update of nomenclature
|
||||
destroy: Delete a nomenclature (cascades to fields)
|
||||
by_code: Get nomenclature by code
|
||||
by_applies_to: Filter nomenclatures by applies_to type
|
||||
"""
|
||||
queryset = Nomenclature.objects.prefetch_related('nomenclaturefield_set').all()
|
||||
serializer_class = NomenclatureSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
filterset_fields = ['applies_to', 'code']
|
||||
search_fields = ['code', 'name']
|
||||
ordering = ['code']
|
||||
|
||||
@action(detail=False, methods=['get'], url_path='by-code')
|
||||
def by_code(self, request):
|
||||
"""Get nomenclature by code query parameter"""
|
||||
code = request.query_params.get('code', None)
|
||||
if not code:
|
||||
return Response(
|
||||
{'detail': 'code parameter is required'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
try:
|
||||
nomenclature = Nomenclature.objects.get(code=code)
|
||||
serializer = self.get_serializer(nomenclature)
|
||||
return Response(serializer.data)
|
||||
except Nomenclature.DoesNotExist:
|
||||
return Response(
|
||||
{'detail': 'Nomenclature not found'},
|
||||
status=status.HTTP_404_NOT_FOUND
|
||||
)
|
||||
|
||||
@action(detail=False, methods=['get'], url_path='by-applies-to')
|
||||
def by_applies_to(self, request):
|
||||
"""Get nomenclatures filtered by applies_to query parameter"""
|
||||
applies_to = request.query_params.get('applies_to', None)
|
||||
if not applies_to:
|
||||
return Response(
|
||||
{'detail': 'applies_to parameter is required'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
if applies_to not in ['vehicle', 'container']:
|
||||
return Response(
|
||||
{'detail': 'applies_to must be either vehicle or container'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
nomenclatures = Nomenclature.objects.filter(applies_to=applies_to)
|
||||
page = self.paginate_queryset(nomenclatures)
|
||||
if page is not None:
|
||||
serializer = self.get_serializer(page, many=True)
|
||||
return self.get_paginated_response(serializer.data)
|
||||
|
||||
serializer = self.get_serializer(nomenclatures, many=True)
|
||||
return Response(serializer.data)
|
||||
Reference in New Issue
Block a user