如何在Django REST Framework中根据关联模型数据过滤主模型并返回匹配结果
Hey there! Let's work through how to filter your MainModel records based on the related ModelAdditionalData_1 and ModelAdditionalData_2 fields using those query parameters. Here's a step-by-step solution:
First, Fix a Tiny Model Typo
I spotted a small mistake in your related models: the OneToOneField references Model, but your main model is named MainModel. Plus, there's a duplicate models. prefix on the DecimalField definitions. Correct those first to avoid errors:
class ModelAdditionalData_1(TimeStampedModel): model_id = models.OneToOneField( MainModel, # Changed from "Model" to "MainModel" verbose_name=_('related model'), on_delete=models.CASCADE, related_name='model_water_flow_data1', related_query_name='model_water_flow_data1' ) model_param_1 = models.DecimalField(_("model_param_1"), max_digits=8) # Fixed duplicate "models." class ModelAdditionalData_2(TimeStampedModel): model_id = models.OneToOneField( MainModel, # Changed from "Model" to "MainModel" verbose_name=_('related model'), on_delete=models.CASCADE, related_name='model_water_flow_data2', related_query_name='model_water_flow_data2' ) model_param_2 = models.DecimalField(_("model_param_2"), max_digits=8) # Fixed duplicate "models."
Update Your View to Handle Filtering
Instead of handling filtering directly in the list method, override the get_queryset method (this follows DRF best practices). We'll use Django's double-underscore lookup to filter across the one-to-one relationships:
from decimal import Decimal, InvalidOperation DETAIL_FILTER_PARAMETERS = [ openapi.Parameter( 'model_param_1', openapi.IN_QUERY, type=openapi.TYPE_STRING, format=openapi.FORMAT_DECIMAL ), openapi.Parameter( 'model_param_2', openapi.IN_QUERY, type=openapi.TYPE_STRING, format=openapi.FORMAT_DECIMAL ) ] @method_decorator(name='list', decorator=swagger_auto_schema( manual_parameters=DETAIL_FILTER_PARAMETERS)) class ModelsViewSet(ReadOnlyModelViewSet): serializer_class = ModelsSerializer queryset = MainModel.objects.all() def get_queryset(self): # Start with the base queryset queryset = super().get_queryset() # Extract query parameters from the request param1 = self.request.query_params.get('model_param_1') param2 = self.request.query_params.get('model_param_2') # Build dynamic filter dictionary filters = {} # Handle model_param_1: validate and add to filters if param1 is not None: try: # Convert string to Decimal to match the field type param1_decimal = Decimal(param1) filters['model_water_flow_data1__model_param_1'] = param1_decimal except InvalidOperation: # Optional: Return empty set or raise a 400 error for invalid input return MainModel.objects.none() # Handle model_param_2 similarly if param2 is not None: try: param2_decimal = Decimal(param2) filters['model_water_flow_data2__model_param_2'] = param2_decimal except InvalidOperation: return MainModel.objects.none() # Apply filters if any are set if filters: queryset = queryset.filter(**filters) return queryset
How This Works
- The
related_namevalues (model_water_flow_data1andmodel_water_flow_data2) let us traverse fromMainModelto its related data using the double-underscore syntax (model_water_flow_data1__model_param_1). - We validate incoming string parameters to convert them to
Decimal(matching your model field type) to avoid type mismatches. - Filters are applied dynamically: if only one parameter is provided, it filters on just that field; if both are provided, it returns records that match both conditions.
Optional: Update Serializer to Include Related Data
If you want the response to include the related ModelAdditionalData fields, update your serializer to nest them:
from rest_framework import serializers class ModelAdditionalData1Serializer(serializers.ModelSerializer): class Meta: model = ModelAdditionalData_1 fields = ['model_param_1'] class ModelAdditionalData2Serializer(serializers.ModelSerializer): class Meta: model = ModelAdditionalData_2 fields = ['model_param_2'] class ModelsSerializer(serializers.ModelSerializer): model_water_flow_data1 = ModelAdditionalData1Serializer() model_water_flow_data2 = ModelAdditionalData2Serializer() class Meta: model = MainModel fields = ['id', 'model_name', 'model_price', 'model_water_flow_data1', 'model_water_flow_data2']
Test Your Filter
Run your server and use the curl command you provided:
curl -X GET "http://localhost:8000/api/v1/models/?model_param_1=1&model_param_2=2" -H "accept: application/json"
This will return only MainModel records where the related ModelAdditionalData_1.model_param_1 equals 1 and ModelAdditionalData_2.model_param_2 equals 2.
内容的提问来源于stack exchange,提问作者MrOldSir

