web-dev-qa-db-fra.com

Zones d'écriture et de lecture uniquement dans la structure de repos Django

J'ai des modèles comme celui-ci:

class ModelA(models.Model):
    name = models.CharField()


class ModelB(models.Model):
    f1 = models.CharField()
    model_a = models.ForeignKey(ModelA)

Sérialiseurs:

class ASerializer(serializers.ModelSerializer):
    model_b_ids = serializers.CharField()
    class Meta:
        model = ModelA
        write_only_fields = ('model_b_ids',)

vues:

class AView(CreateModelMixin, GenericViewSet):

    def perform_create(self, serializer): 
        model_b_ids = parse_somehow(serializer.validated_data["model_b_ids"])
        #do something...

Le problème que je reçois est le avec le "model_b_ids"

L'utilisateur doit l'envoyer lors de l'envoi des données de publication.

Je l'utilise dans perform_create pour créer des liens vers des modèles associés. 

Mais ce n’est pas une "vraie colonne" dans ModelA, donc quand j’essaie de la sauvegarder, elle soulève une exception.

J'ai essayé de le faire passer à validated_data mais encore une fois à obtenir une erreur quelque part qui ne peut pas lire model_b_ids à partir de modèle Une idée sur l'utilisation de ce type de champ correctement?

38
user1305989

Corrigez-moi si je me trompe, mais je ne pense pas que le cadre de repos de Django a un attribut Meta

write_only_fields

Selon leurs docs, vous définissez les champs d'écriture uniquement dans extra_kwargs

par exemple

class UserSerializer(ModelSerializer):
    """
    ``Serializer`` for ``User`` ..
    """

    class Meta:
        model = User
        fields = ('id', 'email', 'first_name', 'last_name' ,'security_question', 'security_question_answer', 'password', 'is_active', 'is_staff')
        read_only_fields = ('is_active', 'is_staff')
        extra_kwargs = {
            'security_question': {'write_only': True},
            'security_question_answer': {'write_only': True},
            'password': {'write_only': True}
        }
49
Dr Manhattan

Conformément au framework Django REST documentation :

L'option write_only_fields sur ModelSerializer a été déplacée vers PendingDeprecation et remplacée par un extra_kwargs plus générique

c'est pourquoi il est recommandé de faire comme ceci: vous devriez utiliser extra_kwargs:

extra_kwargs = {'model_b_ids': {'write_only': True}}

ou:

 model_b_ids = serializers.IntegerField(write_only=True)
35
M.Void

Vous veillez probablement à ce que votre ModelA ait la propriété modelb_set. Dans Django, vous décrivez la relation dans une classe de modèle. Django offre une relation en amont en mettant en casse inférieure le modèle cible et en lui ajoutant le suffixe _set. Alors tu pourrais faire:

a = ModelA.objects.get(pk=1)
a.modelb_set.all()

Cela obtiendrait l'élément avec l'ID (ou la clé primaire) 1 de ModelA et récupérerait tous les éléments ModelB associés.

Vous pouvez définir une valeur pour related_name afin d'écraser la valeur par défaut:

class ModelB(models.Model):
    f1 = models.CharField()
    model_a = models.ForeignKey(ModelA, related_name='model_b')

Dans DRF, vous pouvez légèrement adapter votre sérialiseur:

class ASerializer(serializers.ModelSerializer):
    model_b = serializers.PrimaryKeyRelatedField(many=True, read_only=False)

    class Meta:
        model = ModelA
        write_only_fields = ('model_b',)

Avec serializers.CharField(), vous ne pouvez pas publier de valeurs et les écrire dans le modèle, car ce n'est pas un champ de modèle.

Donnez à cet exemple un essai. Bricoler et expérimenter. Cela devrait vous rapprocher de la solution.

EDIT: Je ne sais pas trop comment Django crée le nom d’une relation en amont pour les noms de classes PascalCase. Est-ce model_b_set pour ModelB? Ou est-ce modelb_set? Vous pouvez essayer de le découvrir.

2
cezar

De docs vous pouvez utiliser read_only

Les champs en lecture seule sont inclus dans la sortie de l'API, mais ne doivent pas être inclus dans l'entrée lors des opérations de création ou de mise à jour. Tous les champs 'read_only' qui sont incorrectement inclus dans l'entrée du sérialiseur seront ignorés.

Définissez-la sur True pour vous assurer que le champ est utilisé lors de la sérialisation d'une représentation, mais pas lors de la création ou de la mise à jour d'une instance lors de la désérialisation.

La valeur par défaut est False

À titre d'exemple:

Nous pouvons l'utiliser sur les champs Serializer:

model_b_ids = serializers.IntegerField(read_only=True)

ou nous pouvons l'utiliser dans extra_kwargs:

extra_kwargs = {
    'model_b_ids': {'read_only': True}
}
1
Druta Ruslan

Vous pouvez remplacer la méthode serializer.save () sur ASerializer pour instancier un objet modelA, définir ses attributs, le sauvegarder, puis définir des relations sur des objets modelB existants, de les sauvegarder également et de réussir au succès. que related_name et RelatedField sur le sérialiseur, comme suggéré, feraient exactement la même chose .... avec moins de frappe ... et globalement mieux :)

0
johnnyB