Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Utilisation de ManyToManyField avec Django Rest Framework

0 votes

Je fais une appli (avec VueJS & Django Rest Framekwork) de prise de notes incluant des tags
donc j'ai pondu un model note avec une relation m2m avec tags

models.py

class Tags(models.Model):
    """
    tags
    """
    user = models.ForeignKey(User)
    tags = models.SlugField(max_length=20)
    date_created = models.DateField(auto_now_add=True)
    date_modified = models.DateField(auto_now=True)

    def __str__(self):
          return self.tags

    class Meta:
           ordering = ('tags',)
           db_table = 'oro_tags'
           unique_together = ("tags", "user")


class Notes(models.Model):

    """
    Notes
    """
    user = models.ForeignKey(User)
    book = models.ForeignKey(Books)
    tags = models.ManyToManyField(Tags)
    url = models.URLField(max_length=255, blank=True)
    title = models.CharField(max_length=200)
    content = models.TextField()
    date_created = models.DateTimeField(auto_now_add=True)
    date_modified = models.DateTimeField(auto_now=True)
    date_deleted = models.DateTimeField(null=True, blank=True)
    status = models.BooleanField(default=True)  # False when deleted

    def __str__(self):
            return "%s - %s" % (self.title, self.book)

    class Meta:
            ordering = ('-date_created', )
           db_table = 'oro_notes'

coté serializers.py ca donne

class TagsSerializer(serializers.ModelSerializer):

    class Meta:
            model = Tags
            fields = ('tags',)


class NoteSerializer(serializers.ModelSerializer):

    # the SlugRelatedField is used to display the tag name instead of tag id
        tags = serializers.SlugRelatedField(
                    many=True,
                    read_only=False,
                    slug_field='tags',
                    required=False,
                    allow_null=True,
                   queryset=Tags.objects.all()
    )

    class Meta:
           model = Notes
           fields = '__all__'

Coté VueJS ca marche impeccablement, je peux créer des notes tranquillement ... tant que les TAGS sont déjà présents dans le model Tags (des tags que j'ai créé par exemple depuis l'url de l'api DRF)
Or quand je créé une note, il va arriver 80% du temps que les tags n'existent pas et du coup quand je créé une note avec un nouveau tag, je me ramasse l'erreur :

Object with tags=tutu does not exist.

edit (21/03) 17:25 à vue de nez je pense que cet erreur remonte du SlugRelatedField qui attend une string et pas une liste.

enfin voici views.py

from orotangi.api.serializers import NoteSerializer, BookSerializer
from orotangi.api.serializers import TagsSerializer
from orotangi.models import Books, Notes, Tags
from rest_framework import viewsets, status
from rest_framework.response import Response


class UserFilterMixin(viewsets.GenericViewSet):

    def get_queryset(self):
    """
    get the data of the current user
    :return:
    """
    qs = super(UserFilterMixin, self).get_queryset()
    return qs.filter(user=self.request.user)


class NoteViewSet(UserFilterMixin, viewsets.ModelViewSet):
    """
    This viewset provides `list`, `create`, `retrieve`, `update` and `destroy` actions.
    """
    queryset = Notes.objects.all()
    serializer_class = NoteSerializer

    def create(self, request, *args, **kwargs):
    """

    :param request:
    :param args:
    :param kwargs:
    :return:
    """
    request.data['user'] = request.user.id
    serializer = self.get_serializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    self.perform_create(serializer)
    headers = self.get_success_headers(serializer.data)
    return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)


class BookViewSet(UserFilterMixin, viewsets.ModelViewSet):
    """
    This viewset provides `list`, `create`, `retrieve`, `update` and `destroy` actions.
    """
    queryset = Books.objects.all()
    serializer_class = BookSerializer


class TagsViewSet(UserFilterMixin, viewsets.ModelViewSet):
    """
    This viewset provides `list`, `retrieve` actions
    """
    queryset = Tags.objects.all()
    serializer_class = TagsSerializer

Qui peut me dire comment avec DRF on créé son enregistrement tranquillou avec un m2m ?

ps: j'ai testé django-taggit-serializer qui ne fonctionne absolument pas.

demandé 20-Mar par foxmask (2,640 points)
edité 21-Mar par foxmask

1 Réponse

0 votes

Tu va être obligé d'override le create de ton NoteSerializer.
DRF doc

répondu 21-Mar par vmonte (188 points)

le truc c'est que je n'utilise pas de "sous serialiser" dans NoteSerializer (pas de TagsSerializer) mais juste un SlugFieldRelated.
Faut-il que je pete ce champ pour justement faire un

class NoteSerializer(serializers.ModelSerializer):

    tags = TagsSerializer() 

    class Meta:
        model = Notes
        fields = '__all__'

à la place ?

Effectivement, je n'avais pas fait gaffe que tu utilisais un SlugFieldRelated.
Je ne l'ai jamais utilisé, mais si tu regarde la méthode tointernalvalue qui est appelé lors de la validation du serializer, tu verras que la méthode lève une exception si l'objet n'existe pas.

Du coup, 2 solutions:

  • Tu utilises ton TagsSerializers
  • Tu créé un autre serializer qui hérite de SlugFieldRelated et tu overide la méthode tointernalvalue
...