Как работать с двойными внешними ключами в Django при создании API?

Я создал эти модели для своей базы данных. Это должно быть что-то вроде дерева (страна>несколько городов>несколько аэропортов). Проблема в том, что я хочу использовать автозаполнение, которое бы показывало не только название аэропорта и название города, но и название страны. Я застрял, потому что не знаю, как добавить страну в мой API (вместе с аэропортами, конечно). Есть ли способ сделать это или, возможно, мне следует изменить архитектуру моей базы данных?

class Country(models.Model):
        name = models.CharField(max_length=100)

    def __str__(self):
        return self.name


class City(models.Model):
    name = models.CharField(max_length=120)
    country = models.ForeignKey(Country, on_delete=models.CASCADE)

    def __str__(self):
        return self.name


class Airport(models.Model):
    name = models.CharField(max_length=150)
    abbr = models.CharField(max_length=4)
    city = models.ForeignKey(City, on_delete=models.CASCADE)
    country = city.country #how can I do this?

    def __str__(self):
        return self.name + ' (' + self.abbr + ')'

из serializers.py:

class AirportSerializer(serializers.ModelSerializer):
    city = serializers.StringRelatedField()

    class Meta:
        model = Airport
        fields = ['name', 'city']

из представлений - создание пользовательского API поиска:

class AirportList(generics.ListAPIView):
    serializer_class =AirportSerializer

    def get_queryset(self):

        airport = self.kwargs['airport']
        list_airports = Airport.objects.filter(name__icontains = airport)
        return list_airports

Путь, если это будет полезно:

re_path('^airports/(?P<airport>.+)/$', views.AirportList.as_view()),

скрипт для автозаполнения:

<script>
    new Autocomplete ('#autocomplete', {
        search: input=>{
            console.log(input)
            const url = `/airports/${input}/`
            return new Promise(resolve =>{
            fetch(url)
            .then(response => response.json())
            .then(data =>{
                const mappedPosts = data.map((airport)=>{return (airport.name + ', ' + airport.city) ;})
                console.log(mappedPosts)
                resolve(mappedPosts)
            })}

            )
        }
    })


</script>

как это выглядит сейчас: enter image description here

Вы можете добавить новое поле в сериализатор и использовать аргумент source для указания способа получения необходимых данных:

class AirportSerializer(serializers.ModelSerializer):
    city = serializers.StringRelatedField()
    country = serializers.CharField(source='city.country.name')

    class Meta:
        model = Airport
        fields = ['name', 'city', 'country']

Обратите внимание, что при этом будет сделан дополнительный запрос для каждого аэропорта в списке. Чтобы избежать этого, вы можете добавить select_related в представлении:

class AirportList(generics.ListAPIView):
    serializer_class =AirportSerializer

    def get_queryset(self):

        airport = self.kwargs['airport']
        list_airports = Airport.objects.filter(name__icontains = airport).select_related('city__country')
        return list_airports
Вернуться на верх