Django REST框架餐饮订单开发遇阻:订单创建功能求助
Hey there! Let's work through your Django REST Framework food ordering project bottleneck together. Based on what you've shared, it sounds like the main sticking point is finalizing the Food Orders model and tying everything together properly. Here's a step-by-step guide to get you back on track:
fake_id) First off, that fake_id field you mentioned is likely meant to link orders to specific food/drink items. Instead of a vague fake ID, we'll create a proper order item junction model to connect your Orders model with individual food or drink products—this is standard for order systems where one order can have multiple items.
First, if you don't already have them, define your core product models:
from django.db import models class Food(models.Model): food_id = models.AutoField(primary_key=True) name = models.CharField(max_length=100) price = models.DecimalField(max_digits=6, decimal_places=2) description = models.TextField(blank=True) class Drink(models.Model): drink_id = models.AutoField(primary_key=True) name = models.CharField(max_length=100) price = models.DecimalField(max_digits=6, decimal_places=2) is_alcoholic = models.BooleanField(default=False)
Now create the OrderItem model (this replaces your incomplete Food Orders model):
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey class OrderItem(models.Model): item_id = models.AutoField(primary_key=True) # Replaces your fake_id with a proper primary key order = models.ForeignKey( 'Orders', on_delete=models.CASCADE, related_name='items' # Lets you access order.items to get all items in an order ) # Use GenericForeignKey to support both Food and Drink items in the same order content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() item = GenericForeignKey('content_type', 'object_id') quantity = models.PositiveIntegerField(default=1) subtotal = models.DecimalField(max_digits=8, decimal_places=2, editable=False) def save(self, *args, **kwargs): # Auto-calculate subtotal when saving the item self.subtotal = self.item.price * self.quantity super().save(*args, **kwargs)
Orders Model Your current Orders model is missing some critical fields and has a risky on_delete setting. Let's update it:
class Orders(models.Model): order_id = models.AutoField(primary_key=True) customer = models.ForeignKey( 'Customers', on_delete=models.PROTECT, # Prevent deleting customers who have orders related_name='orders' ) date = models.DateTimeField(auto_now_add=True) total = models.DecimalField(max_digits=10, decimal_places=2, editable=False) status = models.CharField( max_length=20, choices=[ ('pending', 'Pending'), ('completed', 'Completed'), ('cancelled', 'Cancelled') ], default='pending' ) def save(self, *args, **kwargs): # Auto-calculate total order price from items self.total = sum(item.subtotal for item in self.items.all()) super().save(*args, **kwargs)
Next, we need serializers to handle converting model data to JSON (and vice versa) for your API:
Product Serializers
from rest_framework import serializers from .models import Food, Drink class FoodSerializer(serializers.ModelSerializer): class Meta: model = Food fields = ['food_id', 'name', 'price', 'description'] class DrinkSerializer(serializers.ModelSerializer): class Meta: model = Drink fields = ['drink_id', 'name', 'price', 'is_alcoholic']
Order Item & Order Serializers
from .models import OrderItem, Orders, Customers class OrderItemSerializer(serializers.ModelSerializer): item = serializers.SerializerMethodField() class Meta: model = OrderItem fields = ['item_id', 'item', 'quantity', 'subtotal'] def get_item(self, obj): # Return the correct serializer data based on whether it's food or drink if obj.content_type.model == 'food': return FoodSerializer(obj.item).data elif obj.content_type.model == 'drink': return DrinkSerializer(obj.item).data return None class CustomerSerializer(serializers.ModelSerializer): # Show the associated user's username (adjust based on your Users model) user = serializers.StringRelatedField() class Meta: model = Customers fields = ['id', 'user'] class OrderSerializer(serializers.ModelSerializer): customer = CustomerSerializer(read_only=True) items = OrderItemSerializer(many=True, read_only=True) total = serializers.DecimalField(max_digits=10, decimal_places=2, read_only=True) class Meta: model = Orders fields = ['order_id', 'customer', 'date', 'status', 'total', 'items'] def create(self, validated_data): # Handle creating an order with its items (uses a transaction to avoid partial saves) from django.db import transaction from django.contrib.contenttypes.models import ContentType with transaction.atomic(): # Get items data from the request items_data = self.context['request'].data.get('items', []) # Create the base order order = Orders.objects.create(**validated_data) # Create each order item for item_data in items_data: content_type = ContentType.objects.get(model=item_data['content_type']) product = content_type.get_object_for_this_type(id=item_data['object_id']) OrderItem.objects.create( order=order, content_type=content_type, object_id=item_data['object_id'], quantity=item_data['quantity'] ) # Trigger total calculation order.save() return order
Use Django REST's ModelViewSet to quickly build CRUD endpoints for orders:
ViewSet
from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated from .models import Orders from .serializers import OrderSerializer class OrderViewSet(viewsets.ModelViewSet): serializer_class = OrderSerializer queryset = Orders.objects.all().prefetch_related('items') # Optimize database queries permission_classes = [IsAuthenticated] # Only logged-in users can create orders def perform_create(self, serializer): # Auto-link the order to the current user's Customers profile customer = Customers.objects.get(user=self.request.user) serializer.save(customer=customer)
Routes
Add this to your app's urls.py:
from django.urls import path, include from rest_framework.routers import DefaultRouter from .views import OrderViewSet router = DefaultRouter() router.register(r'orders', OrderViewSet) urlpatterns = [ path('', include(router.urls)), ]
- Transaction Safety: We used
transaction.atomic()in the serializer's create method to ensure that if any part of the order creation fails, nothing gets saved to the database. - Permission Checks: The
IsAuthenticatedpermission ensures only logged-in users can place orders. You can add more granular permissions if needed (e.g., only admins can update order status). - Frontend Payload: When creating an order, your frontend should send a payload like this:
{ "items": [ {"content_type": "food", "object_id": 1, "quantity": 2}, {"content_type": "drink", "object_id": 3, "quantity": 1} ] }
内容的提问来源于stack exchange,提问作者Adrian

