web-dev-qa-db-fra.com

Django Rest framework, comment inclure les champs '__all__' et un champ connexe dans ModelSerializer?

J'ai deux modèles, un avec une relation M2M et un nom apparenté. Je veux inclure tous les champs dans le sérialiseur et le champ associé.

models.py:

class Pizza(models.Model):
    name = models.CharField(max_length=50, unique=True)
    toppings = models.ManyToManyField(Topping, null=True, blank=True, related_name='pizzas')

class Topping(models.Model):
    name = models.CharField(max_length=50, unique=True)
    price = models.IntegerField(default=0)

serializer.py:

class ToppingSerializer(serializers.ModelSerializer):
    class Meta:
        model = Topping
        fields = '__all__' 

Cela fonctionne mais il n'inclut pas le champ associé.

 fields = ['name', 'price', 'pizzas'] 

Cela fonctionne exactement comme je le souhaite, mais que se passe-t-il lorsque le modèle Toppings comporte de nombreux champs. Je veux faire quelque chose comme:

fields = ['__all__', 'pizzas']

Cette syntaxe entraîne une erreur disant:

Nom de domaine __all__ n'est pas valide pour le modèle

Existe-t-il un moyen d'atteindre le comportement souhaité? Ou les champs doivent être saisis manuellement lors de l'utilisation d'un nom associé?

21
Curtwagner1984

Je viens de vérifier le code source de Django Rest Framework. Le comportement que vous souhaitez ne semble pas être pris en charge dans le Framework.

L'option fields doit être une liste, un Tuple ou le texte __all__.

Voici un extrait du code source pertinent:

    ALL_FIELDS = '__all__'
    if fields and fields != ALL_FIELDS and not isinstance(fields, (list, Tuple)):
        raise TypeError(
            'The `fields` option must be a list or Tuple or "__all__". '
            'Got %s.' % type(fields).__name__
        )

Vous ne pouvez pas ajouter 'all' en plus du Tuple ou de la liste avec des champs ...

17
DanEEStar

Comme l'a dit @DanEEStart, DjangoRestFramework n'a pas de moyen simple d'étendre la valeur ' all ' pour les champs, car le get_field_names les méthodes semblent être conçues pour fonctionner de cette façon .

Mais heureusement, vous pouvez remplacer cette méthode pour permettre un moyen simple d'inclure tous les champs et les relations sans énumérer une tonne de champs.

Je remplace cette méthode comme ceci:

class ToppingSerializer(serializers.ModelSerializer):

    class Meta:
        model = Topping
        fields = '__all__'
        extra_fields = ['pizzas']

    def get_field_names(self, declared_fields, info):
        expanded_fields = super(ToppingSerializer, self).get_field_names(declared_fields, info)

        if getattr(self.Meta, 'extra_fields', None):
            return expanded_fields + self.Meta.extra_fields
        else:
            return expanded_fields

Notez que cette méthode modifie uniquement le comportement de ce sérialiseur et que le extra_fields l'attribut ne fonctionne que sur cette classe de sérialiseur.

Si vous avez des tonnes de sérialiseur comme celui-ci, vous pouvez créer une classe intermédiaire pour inclure ce get_fields_names méthode en un seul endroit et réutilisation plusieurs fois. Certains aiment ça:

class CustomSerializer(serializers.HyperlinkedModelSerializer):

    def get_field_names(self, declared_fields, info):
        expanded_fields = super(CustomSerializer, self).get_field_names(declared_fields, info)

        if getattr(self.Meta, 'extra_fields', None):
            return expanded_fields + self.Meta.extra_fields
        else:
            return expanded_fields


class ToppingSerializer(CustomSerializer):

    class Meta:
        model = Topping
        fields = '__all__'
        extra_fields = ['pizzas']

class AnotherSerializer(CustomSerializer):

    class Meta:
        model = Post
        fields = '__all__'
        extra_fields = ['comments']
25
hugoruscitti

Un vieux problème mais pensait que cela pourrait aider d'autres à l'avenir.

Je viens de rencontrer un problème similaire et j'ai obtenu l'option " all " en spécifiant manuellement un champ supplémentaire selon l'exemple suivant. Je ne sais pas si cela résoudrait également votre problème. C'est une foutue vue plus propre que tout ce que j'ai vu.

http://www.Django-rest-framework.org/api-guide/relations/#nested-relationships

class TrackSerializer(serializers.ModelSerializer):
    class Meta:
        model = Track
        fields = '__all__'

class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackSerializer(many=True, read_only=True)

    class Meta:
        model = Album
        fields = '__all__'

Je suppose que cela fonctionnerait pour toutes les autres options de champ connexes répertoriées sur la même page: http://www.Django-rest-framework.org/api-guide/relations/#serializer-relations =

J'utilise le Django Rest Framework version 3.6.2

Exemple de relation inverse comme demandé:

class TrackSerializer(serializers.ModelSerializer):
    album = AlbumSerializer(source='album_id')

    class Meta:
        model = Track
        fields = '__all__'
12
Aiky30

Salut, je pourrais atteindre le résultat attendu en utilisant API _meta de Django , qui semble être disponible depuis Django 1.11. Donc, dans mon sérialiseur, j'ai fait:

model = MyModel
fields = [field.name for field in model._meta.fields]
fields.append('any_other_field')

En programmation, il y a toujours plusieurs façons d'obtenir le même résultat, mais celui ci-dessus, a vraiment fonctionné pour moi.

À votre santé!

6
Wand

pour inclure tous les champs et les autres champs définis dans votre sérialiseur, vous pouvez simplement dire exclude = ()

class ToppingSerializer(serializers.HyperlinkedModelSerializer):
   pizzas = '<>' #the extra attribute value
    class Meta:
        model = Topping
        exclude = ()

Ceci listera toutes les valeurs de champ avec l'argument supplémentaire pizzas

2
riyasyash

Voilà comment je l'ai fait, beaucoup plus facile

class OperativeForm(forms.ModelForm):
    class Meta:
        model = Operative
        fields = '__all__'
        exclude = ('name','objective',)
        widgets = {'__all__':'required'}
0