You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django模型中使用抽象父类进行过滤或查询?

Hey there! Let's break down how to make this work for your Django DRF setup. Since you're using an abstract PrimaryAddress model with subclasses like MedicalStorePrimaryAddress (and more coming), here's a step-by-step approach to query all related entities filtered by city, latitude/longitude, and distance:

1. Combine QuerySets from All Subclasses

Because abstract models don't create their own database tables, each subclass (like MedicalStorePrimaryAddress, HospitalPrimaryAddress) lives in its own table. To query across all of them, you'll need to union their querysets:

from .models import MedicalStorePrimaryAddress, HospitalPrimaryAddress

# Union all address subclass querysets
combined_addresses = MedicalStorePrimaryAddress.objects.all() | HospitalPrimaryAddress.objects.all()

As you add more subclasses later, just extend this union with the new model's queryset.

2. Add City Filtering

Filtering by city is straightforward—just chain a filter() call to the combined queryset. In a DRF view, you'll typically pull the city from request query parameters:

def get_queryset(self):
    combined_addresses = MedicalStorePrimaryAddress.objects.all() | HospitalPrimaryAddress.objects.all()
    
    # Filter by city if provided in query params (e.g., ?city=NewYork)
    city = self.request.query_params.get('city')
    if city:
        combined_addresses = combined_addresses.filter(city__iexact=city)
    
    return combined_addresses
3. Implement Distance Filtering

To filter by distance from a given latitude/longitude, you can use a custom database function to calculate the Haversine distance (works with MySQL and PostgreSQL with minor tweaks). First, define the function:

from django.db.models import F, Func, FloatField

class Haversine(Func):
    function = 'HAVERSINE'
    output_field = FloatField()

Then, in your view, add distance filtering using the user-provided lat/lon:

def get_queryset(self):
    combined_addresses = MedicalStorePrimaryAddress.objects.all() | HospitalPrimaryAddress.objects.all()
    
    # City filter (as before)
    city = self.request.query_params.get('city')
    if city:
        combined_addresses = combined_addresses.filter(city__iexact=city)
    
    # Distance filter (e.g., ?latitude=40.7128&longitude=-74.0060&distance=10)
    lat = self.request.query_params.get('latitude')
    lon = self.request.query_params.get('longitude')
    max_distance = self.request.query_params.get('distance', 10)  # Default to 10km
    
    if lat and lon:
        try:
            lat_float = float(lat)
            lon_float = float(lon)
            max_distance_float = float(max_distance)
            
            combined_addresses = combined_addresses.annotate(
                distance=Haversine(F('latitude'), F('longitude'), lat_float, lon_float)
            ).filter(distance__lte=max_distance_float)
        except ValueError:
            # Handle invalid number inputs gracefully
            pass
    
    return combined_addresses

Note: For PostgreSQL, you might need to use ST_Distance instead of HAVERSINE (you'll need PostGIS installed for that). Adjust the Haversine function accordingly if you're using a different database.

4. Handle Polymorphic Serialization

Since your queryset includes multiple address subclasses, you'll need a serializer that can handle different model types. You can either build a custom serializer or use the django-rest-polymorphic package for simplicity.

Custom Polymorphic Serializer

from rest_framework import serializers
from .models import MedicalStorePrimaryAddress, HospitalPrimaryAddress

class MedicalStoreAddressSerializer(serializers.ModelSerializer):
    class Meta:
        model = MedicalStorePrimaryAddress
        fields = '__all__'

class HospitalAddressSerializer(serializers.ModelSerializer):
    class Meta:
        model = HospitalPrimaryAddress
        fields = '__all__'

class AddressSerializer(serializers.Serializer):
    def to_representation(self, instance):
        if isinstance(instance, MedicalStorePrimaryAddress):
            return MedicalStoreAddressSerializer(instance).data
        elif isinstance(instance, HospitalPrimaryAddress):
            return HospitalAddressSerializer(instance).data
        # Add new subclass serializers here as you create them
        return super().to_representation(instance)

Using django-rest-polymorphic

Install the package first, then define a polymorphic serializer:

pip install django-rest-polymorphic
from rest_polymorphic.serializers import PolymorphicSerializer
from .models import PrimaryAddress, MedicalStorePrimaryAddress, HospitalPrimaryAddress

class AddressPolymorphicSerializer(PolymorphicSerializer):
    model_serializer_mapping = {
        MedicalStorePrimaryAddress: MedicalStoreAddressSerializer,
        HospitalPrimaryAddress: HospitalAddressSerializer,
        # Add more mappings as you add subclasses
    }
5. Optional: Switch to Concrete Base Model

If you find unioning querysets cumbersome long-term, consider changing PrimaryAddress from an abstract model to a concrete one, then use multi-table inheritance for subclasses. This lets you query directly from the parent model:

class PrimaryAddress(models.Model):
    latitude = models.FloatField()
    longitude = models.FloatField()
    city = models.CharField(max_length=100)
    # Other shared fields
    # Remove abstract = True

class MedicalStorePrimaryAddress(PrimaryAddress):
    # Medical store-specific fields
    store_id = models.ForeignKey(MedicalStore, on_delete=models.CASCADE)

Now you can query PrimaryAddress.objects.all() and filter directly, then use select_related to fetch subclass data if needed.


内容的提问来源于stack exchange,提问作者Maverick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:47:48