Skip to content

Commit b25ae6d

Browse files
added order model and api files
1 parent dc303d8 commit b25ae6d

File tree

8 files changed

+184
-24
lines changed

8 files changed

+184
-24
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ __pycache__/
88
env/
99
__pycache__/
1010
env/
11+
env/
12+
__pycache__/
13+
*.pyc

api/serializers.py

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,47 @@
11
from rest_framework import serializers
2-
from orders.models import Order, WasteClaim
2+
from orders.models import Order, OrderItem, WasteClaim
3+
4+
5+
6+
class OrderItemSerializer(serializers.ModelSerializer):
7+
class Meta:
8+
model = OrderItem
9+
fields = ['id', 'order', 'quantity', 'price']
10+
11+
def validate_quantity(self, value):
12+
if value <= 0:
13+
raise serializers.ValidationError("Quantity must be a positive integer.")
14+
return value
315

416
class OrderSerializer(serializers.ModelSerializer):
17+
items = OrderItemSerializer(many=True, required=False, read_only=True)
18+
519
class Meta:
620
model = Order
7-
fields = [
8-
'order_id', 'order_date', 'order_status', 'payment_status',
9-
'total_amount', 'pin', 'created_at', 'updated_at'
10-
]
11-
read_only_fields = ['order_id', 'pin', 'order_date', 'created_at', 'updated_at']
21+
fields = ['order_id', 'order_date', 'order_status', 'payment_status', 'total_amount', 'pin', 'items']
22+
read_only_fields = ['order_id', 'order_date', 'total_amount', 'pin']
23+
24+
def create(self, validated_data):
25+
items_data = validated_data.pop('items', [])
26+
order = Order.objects.create(total_amount=0, **validated_data)
27+
for item_data in items_data:
28+
OrderItem.objects.create(order=order, **item_data)
29+
order.update_total_amount()
30+
return order
31+
32+
def update(self, instance, validated_data):
33+
34+
allowed = ['order_status', 'payment_status']
35+
for field in list(validated_data.keys()):
36+
if field not in allowed:
37+
validated_data.pop(field)
38+
for attr, value in validated_data.items():
39+
setattr(instance, attr, value)
40+
instance.save()
41+
return instance
1242

1343
class WasteClaimSerializer(serializers.ModelSerializer):
1444
class Meta:
1545
model = WasteClaim
16-
fields = [
17-
'waste_id', 'claim_status', 'claim_time', 'pickup_window_end',
18-
'pin', 'created_at', 'updated_at'
19-
]
20-
read_only_fields = ['waste_id', 'pin', 'claim_time', 'created_at', 'updated_at']
21-
22-
46+
fields = '__all__'
47+
read_only_fields = ['waste_id', 'pin', 'created_at', 'updated_at']

api/urls.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
from django.urls import path, include
22
from rest_framework.routers import DefaultRouter
3-
from .views import OrderViewSet, WasteClaimViewSet
3+
from .views import OrderViewSet, WasteClaimViewSet, OrderItemViewSet
44

55
router = DefaultRouter()
66
router.register(r'orders', OrderViewSet)
7+
router.register(r'item', OrderItemViewSet)
78
router.register(r'wasteclaims', WasteClaimViewSet)
89

910
urlpatterns = [

api/views.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,51 @@
1-
from django.shortcuts import render
2-
from django.utils import timezone
31
from rest_framework import viewsets
4-
from orders.models import Order, WasteClaim
5-
from .serializers import OrderSerializer, WasteClaimSerializer
2+
from rest_framework.permissions import AllowAny
3+
from rest_framework.permissions import IsAuthenticated
4+
from orders.models import Order, WasteClaim, OrderItem
5+
from .serializers import OrderSerializer, WasteClaimSerializer, OrderItemSerializer
6+
from orders.permissions import OrderPermission,WasteClaimPermission
7+
from django.utils import timezone
68

79
class OrderViewSet(viewsets.ModelViewSet):
810
queryset = Order.objects.all()
911
serializer_class = OrderSerializer
12+
permission_classes = [AllowAny]
13+
http_method_names = ['get', 'post', 'put', 'patch', 'head', 'options']
14+
15+
# def get_queryset(self):
16+
# user = self.request.user
17+
18+
# if user.is_staff or getattr(user, 'role', None) == 'producer':
19+
# return Order.objects.all()
20+
21+
# return Order.objects.filter(user=user)
22+
23+
# def perform_create(self, serializer):
24+
# serializer.save(user=self.request.user)
25+
1026

27+
class OrderItemViewSet(viewsets.ModelViewSet):
28+
queryset = OrderItem.objects.all()
29+
serializer_class = OrderItemSerializer
30+
permission_classes = [AllowAny]
31+
32+
def get_queryset(self):
33+
order_id = self.request.query_params.get('order_id')
34+
if order_id:
35+
return self.queryset.filter(order_id=order_id)
36+
return self.queryset
37+
38+
39+
40+
1141
class WasteClaimViewSet(viewsets.ModelViewSet):
1242
queryset = WasteClaim.objects.all()
1343
serializer_class = WasteClaimSerializer
44+
permission_classes = [AllowAny]
45+
http_method_names = ['get', 'post', 'put', 'patch', 'head', 'options']
1446

1547
def perform_create(self, serializer):
1648
serializer.save(claim_time=timezone.now())
1749

50+
51+

feedlink/settings.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,18 @@
3939
'django.contrib.staticfiles',
4040
'orders',
4141
'rest_framework',
42+
'rest_framework.authtoken',
4243
]
4344

44-
rest_FRAMEWORK ={
45-
"DEFAULT_RENDERER_CLASSES": [
46-
"rest_framework.renderers.JSONRenderer",
47-
]
45+
REST_FRAMEWORK = {
46+
# 'DEFAULT_AUTHENTICATION_CLASSES': [
47+
# 'rest_framework.authentication.TokenAuthentication',
48+
# # or 'rest_framework_simplejwt.authentication.JWTAuthentication' if using JWT
49+
# ],
50+
'DEFAULT_PERMISSION_CLASSES': [
51+
# 'rest_framework.permissions.IsAuthenticated',
52+
'rest_framework.permissions.AllowAny',
53+
],
4854
}
4955

5056
MIDDLEWARE = [
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Generated by Django 5.2.6 on 2025-09-10 05:46
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('orders', '0003_alter_order_pin'),
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name='OrderItem',
16+
fields=[
17+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18+
('quantity', models.PositiveIntegerField()),
19+
('price', models.DecimalField(decimal_places=2, max_digits=10)),
20+
('created_at', models.DateTimeField(auto_now_add=True)),
21+
('updated_at', models.DateTimeField(auto_now=True)),
22+
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='orders.order')),
23+
],
24+
),
25+
]

orders/models.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ class Order(models.Model):
1717

1818
order_id = models.AutoField(primary_key=True)
1919
# user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='orders')
20-
# listing = models.ForeignKey(Inventory, on_delete=models.CASCADE)
2120
order_date = models.DateTimeField(default=timezone.now)
2221
order_status = models.CharField(max_length=10, choices=STATUS, default='pending')
2322
payment_status = models.CharField(max_length=10, choices=PAYMENT_STATUS, default='unpaid')
@@ -33,9 +32,34 @@ def save(self, *args, **kwargs):
3332
pin = generate_pin()
3433
self.pin = pin
3534
super().save(*args, **kwargs)
35+
self.update_total_amount()
36+
37+
def update_total_amount(self):
38+
total = self.items.aggregate(
39+
total=models.Sum(models.F('quantity') * models.F('price'), output_field=models.DecimalField())
40+
)['total'] or 0
41+
if self.total_amount != total:
42+
self.total_amount = total
43+
super().save(update_fields=['total_amount'])
44+
45+
def __str__(self):
46+
return f"Order {self.order_id}"
47+
48+
49+
class OrderItem(models.Model):
50+
order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items')
51+
# listing = models.ForeignKey(Inventory, on_delete=models.CASCADE)
52+
quantity = models.PositiveIntegerField()
53+
price = models.DecimalField(max_digits=10, decimal_places=2)
54+
created_at = models.DateTimeField(auto_now_add=True)
55+
updated_at = models.DateTimeField(auto_now=True)
56+
57+
def save(self, *args, **kwargs):
58+
super().save(*args, **kwargs)
59+
self.order.update_total_amount()
3660

3761
def __str__(self):
38-
return f"Order {self.order_id} "
62+
return f"OrderItem {self.id} for Order {self.order.order_id}"
3963

4064
class WasteClaim(models.Model):
4165
STATUS = [('pending', 'Pending'), ('collected', 'Collected')]

orders/permissions.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from rest_framework.permissions import BasePermission, SAFE_METHODS
2+
3+
class OrderPermission(BasePermission):
4+
5+
def has_permission(self, request, view):
6+
user = request.user
7+
if not user or not user.is_authenticated:
8+
return False
9+
if request.method in SAFE_METHODS:
10+
return True
11+
if request.method == 'POST':
12+
13+
is_buyer = not user.is_staff and getattr(user, 'role', None) != 'producer'
14+
return is_buyer
15+
if request.method in ['PUT', 'PATCH']:
16+
return user.is_staff or getattr(user, 'role', None) == 'producer'
17+
if request.method == 'DELETE':
18+
return False
19+
return False
20+
21+
def has_object_permission(self, request, view, obj):
22+
return self.has_permission(request, view)
23+
24+
25+
class WasteClaimPermission(BasePermission):
26+
27+
def has_permission(self, request, view):
28+
user = request.user
29+
if not user or not user.is_authenticated:
30+
return False
31+
if request.method in SAFE_METHODS:
32+
return True
33+
if request.method == 'POST':
34+
return getattr(user, 'role', None) == 'recycler'
35+
if request.method in ['PUT', 'PATCH']:
36+
return user.is_staff or getattr(user, 'role', None) == 'producer'
37+
if request.method == 'DELETE':
38+
return False
39+
return False
40+
41+
def has_object_permission(self, request, view, obj):
42+
return self.has_permission(request, view)

0 commit comments

Comments
 (0)