Как работать с двойными внешними ключами в 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>
Вы можете добавить новое поле в сериализатор и использовать аргумент 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