基于Django Rest Framework设计只读RESTful API的需求咨询
Got it, let's walk through building this GET-only API endpoint with Django Rest Framework step by step. I'll cover both a database-backed approach (best for persistent, queryable data) and a direct CSV-read approach (if you want to skip setting up a database):
This method stores your CSV data in Django models, making it easy to query, update, and maintain over time.
Step 1: Setup & Model Definitions
First, ensure you have Django and DRF installed:
pip install django djangorestframework
Add rest_framework to your INSTALLED_APPS in settings.py:
INSTALLED_APPS = [ # ... other apps 'rest_framework', 'your_app_name', # Replace with your actual app name ]
Define your models in models.py to represent cars and their descriptions:
from django.db import models class Car(models.Model): model = models.CharField(max_length=100, unique=True) company = models.CharField(max_length=100) def __str__(self): return f"{self.model} - {self.company}" class CarDescription(models.Model): car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='details') year = models.IntegerField() condition = models.CharField(max_length=50) description = models.TextField() def __str__(self): return f"{self.car.model} ({self.year}) - {self.condition}"
Run migrations to create the database tables:
python manage.py makemigrations python manage.py migrate
Step 2: Create Serializers
We'll use nested serializers to structure the response exactly like your example. Create a serializers.py file:
from rest_framework import serializers from .models import Car, CarDescription class CarDescriptionSerializer(serializers.ModelSerializer): class Meta: model = CarDescription fields = ['condition', 'description'] class CarYearSerializer(serializers.Serializer): model = serializers.CharField() company = serializers.CharField() year = serializers.IntegerField() details = CarDescriptionSerializer(many=True)
Step 3: Import CSV Data
Create a custom management command to bulk-import your CSV data. In your app, create the structure management/commands/import_cars.py:
import csv from django.core.management.base import BaseCommand from your_app_name.models import Car, CarDescription # Replace with your app name class Command(BaseCommand): help = 'Imports car data from cars.csv and description.csv' def handle(self, *args, **kwargs): # Import cars from cars.csv with open('cars.csv', 'r') as f: reader = csv.reader(f) for row in reader: model, company = [item.strip() for item in row] Car.objects.get_or_create(model=model, company=company) self.stdout.write(self.style.SUCCESS('Cars imported successfully')) # Import descriptions from description.csv (assumes columns: model, year, condition, description) with open('description.csv', 'r') as f: reader = csv.reader(f) for row in reader: model, year, condition, description = [item.strip() for item in row] try: car = Car.objects.get(model=model) CarDescription.objects.get_or_create( car=car, year=int(year), condition=condition, description=description ) except Car.DoesNotExist: self.stdout.write(self.style.WARNING(f"Skipping description for unknown model: {model}")) self.stdout.write(self.style.SUCCESS('Descriptions imported successfully'))
Run the import command:
python manage.py import_cars
Step 4: Build the View
Create a view that handles GET requests, filters by model (and optional year), and returns structured data. In views.py:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .models import Car, CarDescription from .serializers import CarYearSerializer class CarDetailAPIView(APIView): def get(self, request): # Get model from query parameter (e.g., /api/cars/?model=Escalade) model = request.query_params.get('model') if not model: return Response( {"error": "Please provide a 'model' query parameter (e.g., ?model=Escalade)"}, status=status.HTTP_400_BAD_REQUEST ) try: car = Car.objects.get(model__iexact=model) except Car.DoesNotExist: return Response( {"error": f"Car model '{model}' not found"}, status=status.HTTP_404_NOT_FOUND ) # Optional: filter by year if provided year_filter = request.query_params.get('year') descriptions = car.details.all() if year_filter: try: descriptions = descriptions.filter(year=int(year_filter)) except ValueError: return Response( {"error": "Year must be an integer (e.g., ?year=2018)"}, status=status.HTTP_400_BAD_REQUEST ) # Structure data to match your example year_groups = {} for desc in descriptions: if desc.year not in year_groups: year_groups[desc.year] = { "model": car.model, "company": car.company, "year": desc.year, "details": [] } year_groups[desc.year]["details"].append({ "condition": desc.condition, "description": desc.description }) # Return single entry if year is specified, else all years response_data = list(year_groups.values()) if year_filter and response_data: response_data = response_data[0] return Response(response_data)
Step 5: Configure URLs
Add the endpoint to your project's urls.py:
from django.urls import path from your_app_name.views import CarDetailAPIView # Replace with your app name urlpatterns = [ # ... other URLs path('api/cars/', CarDetailAPIView.as_view(), name='car-details'), ]
Test the API
Start your Django server:
python manage.py runserver
Test the endpoint with these requests:
- Get all years for Escalade:
http://localhost:8000/api/cars/?model=Escalade - Get 2018 Escalade data:
http://localhost:8000/api/cars/?model=Escalade&year=2018
The response will match your desired format, e.g.:
{ "model": "Escalade", "company": "Ford", "year": 2018, "details": [ { "condition": "new", "description": "perfect condition" } ] }
If you don't want to use a database (great for static, rarely updated data), modify the view to read directly from your CSV files:
import csv from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status class CarDetailAPIView(APIView): def get(self, request): model = request.query_params.get('model') if not model: return Response( {"error": "Please provide a 'model' query parameter"}, status=status.HTTP_400_BAD_REQUEST ) # Fetch company from cars.csv company = None with open('cars.csv', 'r') as f: reader = csv.reader(f) for row in reader: if row[0].strip().lower() == model.lower(): company = row[1].strip() break if not company: return Response( {"error": f"Car model '{model}' not found"}, status=status.HTTP_404_NOT_FOUND ) # Fetch and structure descriptions from description.csv year_filter = request.query_params.get('year') year_groups = {} with open('description.csv', 'r') as f: reader = csv.reader(f) for row in reader: row_model, row_year, condition, desc_text = [item.strip() for item in row] if row_model.lower() != model.lower(): continue # Apply year filter if provided if year_filter and int(row_year) != int(year_filter): continue year = int(row_year) if year not in year_groups: year_groups[year] = { "model": model, "company": company, "year": year, "details": [] } year_groups[year]["details"].append({ "condition": condition, "description": desc_text }) response_data = list(year_groups.values()) if year_filter and response_data: response_data = response_data[0] return Response(response_data if response_data else {})
Note: Make sure your CSV files are in your Django project root, or update the file paths in the code to match their location.
内容的提问来源于stack exchange,提问作者wsvincent

