Динамическая сериализация для связанных полей
У меня есть следующие модели:
class Country(models.Model):
"""
Country model
"""
# With name and isoCode (charfield)
...
class State(models.Model):
"""
State model
"""
# With name and isoCode (charfield)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
...
class City(models.Model):
"""
City model
"""
name = models.CharField(max_length=32)
state = models.ForeignKey(State, on_delete=models.CASCADE)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
...
And UserLocation referenced by:
class Location(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="location")
country = models.ForeignKey(Country, on_delete=models.CASCADE)
state = models.ForeignKey(State, on_delete=models.CASCADE)
city = models.ForeignKey(City, on_delete=models.CASCADE)
Как создать сериализатор, который создает UserLocation, а также возвращает информацию в JSON?
Я пытался
class LocationSerializer(serializers.ModelSerializer):
country = serializers.SlugRelatedField(slug_field="isoCode", queryset=Country.objects.all())
state = serializers.SlugRelatedField(slug_field="isoCode", queryset=State.objects.filter(country__isoCode=country))
city = serializers.SlugRelatedField(slug_field="name", queryset=City.objects.all())
class Meta:
model = Location
fields = ["user", "country", "state", "city"]
Но это не работает, выдает ошибку
{"state":["Object with isoCode=BC does not exist."],...
Как создать динамически связанный сериализатор? Или как это можно обойти?
Думаю, вам нужен следующий сериализатор
class LocationSerializer(serializers.ModelSerializer):
country = serializers.ReadOnlyField(source="country.isocode")
state = serializers.ReadOnlyField(source="state.isocode")
city = serializers.ReadOnlyField(source="city.name")
class Meta:
model = Location
fields = ["user", "country", "state", "city"]
Однако, похоже, что ваша модель спроектирована неправильно. Вам не нужно иметь внешние ключи country, state в модели Location, так как модель City имеет state, модель State имеет country.
Для будущих пользователей. Я обнаружил, что
class LocationSerializer(serializers.ModelSerializer):
'''Used to serialize user location'''
country = serializers.SlugRelatedField(slug_field="isoCode", queryset=Country.objects.all(), required=True)
class Meta:
model = Location
fields = ["user", "country"]
extra_kwargs = {"user": { "validators":[UniqueValidator(queryset=Location.objects.all(), message=_("User already has a location. Please update user location. Hint: use PUT request"))]}}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if "country" in kwargs['context']['request'].data and len(Country.objects.get(isoCode=self.initial_data["country"]).state_set.all()) > 0:
self.fields["state"] = serializers.SlugRelatedField(slug_field="isoCode", queryset=State.objects.filter(country__isoCode=self.initial_data["country"]), required=True)
if "state" in kwargs['context']['request'].data and len(State.objects.get(isoCode=self.initial_data["state"], country__isoCode=self.initial_data["country"]).city_set.all()) > 0:
self.fields["city"] = serializers.SlugRelatedField(slug_field="name", queryset=City.objects.filter(state__isoCode=self.initial_data["state"],country__isoCode=self.initial_data["country"]), required=True)
Показалось, что это сработало в моем случае