Jak zmienić nazwę pola w Django REST Framework

98

Próbuję zmienić nazwę pola modelu w serializatorze DRF, jak alias w języku SQL. Próbowałem różnych metod, ale nie mogę.

models.py

class Park(models.Model):
    name = models.CharField(max_length=256)
    alternate_name = models.CharField(max_length=256, blank=True)
    objects = models.GeoManager()

    class Meta:
        db_table = u'p_park'

    def __unicode__(self):
        return '%s' % self.name

    def alias_alternate_name(self):
        return self.alternate_name

serializers.py

class ParkSerializer(serializers.ModelSerializer):

    location = serializers.Field(source='alias_alternate_name')
    #location = serializers.SerializerMethodField(source='alias_alternate_name')

    #alternate_name as location


    class Meta:
        model = Park
        fields = ('id', 'name', 'location')

Próbowałem również dodać alias w Django Queryset, ale nie mogę tego zmienić.

Zaktualizowano

To jest wyjątek, przed którym stoję

AttributeError at / ViewName / obiekt „module” nie ma atrybutu „Field”

Jak mogę to zrobić?

Shoaib Ijaz
źródło
1
Czy używasz prawidłowej implementacji tego serializers.SerializerMethodFieldpodejścia? Mam na myśli to: serializers.SerializerMethodField('get_location')idef get_location(self, obj): ...
erthalion
Czy możemy zobaczyć import serializers.py?
joerick,
zagłosuje negatywnie na pytanie, ponieważ OP zaakceptował częściowo błędną i mylącą odpowiedź zamiast lepszych poniżej ...
NeuronQ

Odpowiedzi:

59

Możesz użyć serializers.SerializerMethodField:

Oto model Park, który ma pola name i alternate_name.

class Park(models.Model):
    name = models.CharField(max_length=256)
    alternate_name = models.CharField(max_length=256, blank=True)
    objects = models.GeoManager()

    class Meta:
        db_table = u'p_park'

    def __unicode__(self):
        return '%s' % self.name

Oto Serializer for Park Model, ParkSerializer. Spowoduje to zmianę nazwy alternate_name na lokalizację.

class ParkSerializer(serializers.ModelSerializer):
    location = serializers.SerializerMethodField('get_alternate_name')

    class Meta:
        model = Park
        fields = ('other_fields', 'location')

    def get_alternate_name(self, obj):
        return obj.alternate_name

Dodatkowo możesz użyć serializers.CharFieldz sourceatrybutem:

class ParkSerializer(serializers.ModelSerializer):
    location = serializers.CharField(source='other_fields')

    class Meta:
        model = Park
        fields = ('other_fields', 'location')

Zapis Django __do przechodzenia przez klucz obcy również działa:

location = serializers.CharField(source='OtherModel__other_fields')

Ta sama zasada ma zastosowanie, jeśli chcesz zmienić typ zwracania w interfejsie API, więc możesz to zrobić serializers.DecimalField(source=...) a także inne typy pól.

Działa to jednak tylko w przypadku pól tylko do odczytu.

erthalion
źródło
Teraz ten wyjątek rzuca AttributeError w / ViewName / obiekt 'module' nie ma atrybutu 'SerializerMethodField'
Shoaib Ijaz
1
Jak przebiegałby ten trening z tworzeniem i edytowaniem żądań?
iankit
1
Wiersz nr 13 „Zen of Python”: „Powinien być jeden - a najlepiej tylko jeden - oczywisty sposób na zrobienie tego”.
iankit
14
To nie powinna być akceptowana odpowiedź. Zobacz ten poniżej, który ma prawie 5 razy więcej głosów za w momencie, gdy to piszę.
cderwin
6
To jest złe rozwiązanie. sourceZamiast tego użyj kwarg, jak opisano poniżej.
Patrick,
215

Istnieje bardzo fajna funkcja w polach serializatorów i serializatorach ogólnie nazywana „źródłem”, w której można określić źródło danych z pola modelu.

class ParkSerializer(serializers.ModelSerializer):
    location = serializers.SomeSerializerField(source='alternate_name')

    class Meta:
        model = Park
        fields = ('other_fields', 'location')

Gdzie serializers.SomeSerializerField może być serializers.CharField, jak sugeruje twój model, ale może również być przez dowolne inne pola. Możesz także zamiast tego umieścić pola relacyjne i inne serializatory, a to nadal będzie działać jak urok. tj. nawet jeśli alternate_name było polem klucza obcego w innym modelu.

class ParkSerializer(serializers.ModelSerializer):
    locations = AlternateNameSerializer(source='alternate_name', many=true)

    class Meta:
        model = Park
        fields = ('other_fields', 'locations')

class AlternateNameSerializer(serializers.ModelSerialzer):
    class Meta:
        model = SomeModel

Działa to również przy tworzeniu, usuwaniu i modyfikowaniu żądań. Skutecznie tworzy mapowanie jeden do jednego nazwy pola w serializatorze i nazwę pola w modelach.

iankit
źródło
Zgodziłem się, to sourcejest bardziej ogólne podejście. Ale w pytaniu widać kilka prób użycia go, więc jeśli chcesz odpowiedzieć w ten sposób, powinieneś również wyjaśnić, dlaczego oryginalny kod nie działa, prawda?
erthalion
Twój kod będzie działał dobrze ... o ile żądanie dotyczy listy i pobierania
iankit
Obie odpowiedzi są niepełne. W przypadku klucza obcego metoda ta zakłada, że ​​tworząc nowy Park, musisz podać cały obiekt nadrzędny (nazwa_alternatywna) jako dykt w żądaniu POST, co jest szalone, ponieważ obiekt nadrzędny już istnieje. Należy umieć wspomnieć o zagranicznej instancji poprzez jej id.
stelios
W moim przypadku (klucz obcy) rozwiązałem ten problem z locations = serializers.PrimaryKeyRelatedField(source='alternate_name', queryset=AlternateName.objects.all()). Najwyraźniej RelatedFieldmożna go również użyć.
stelios
@chefarov source = 'nowa_nazwa' to ogólny argument, który można podać polom serializatora, relacjom i innym powiązanym serializatorom itp. Nie jestem pewien, dlaczego twierdzisz, że odpowiedź jest niekompletna.
iankit
15

To zadziała również dla operacji zapisu

class ParkSerializer(serializers.ModelSerializer):
    location = serializers.CharField(source='alternate_name')

    class Meta:
        model = Park
        fields = ('id', 'name', 'location')
vijay
źródło