Using Django-taggit with django-rest-framework, i'm not able to save my tags
django, django-rest-framework, django-taggit
Solution
meet the same question. But I just want to save the tag list directly by TaggableManager (without TagListSerializer and TagsListAPIView). My solution is:
class MyModel(models.Model):
...
tags = TaggableManager(blank=True)
def get_tags_display(self):
return self.tags.values_list('name', flat=True)
class MyModelSerializer(serializers.HyperlinkedModelSerializer):
...
tags = serializers.Field(source='get_tags_display') # more about: http://www.django-rest-framework.org/api-guide/fields#generic-fields
...
class MyModelViewSet(viewsets.ModelViewSet):
...
def post_save(self, *args, **kwargs):
if 'tags' in self.request.DATA:
self.object.tags.set(*self.request.DATA['tags']) # type(self.object.tags) == <taggit.managers._TaggableManager>
return super(MyModelViewSet, self).post_save(*args, **kwargs)
The post data of tags data will be ['tagA', 'tagB',...], the TaggableManager will handle it. Thx.
For DRF>3.1, you just need to override create and update in your ModelSerializer class:
class StringListField(serializers.ListField): # get from http://www.django-rest-framework.org/api-guide/fields/#listfield
child = serializers.CharField()
def to_representation(self, data):
return ' '.join(data.values_list('name', flat=True)) # you change the representation style here.
class MyModelSerializer(serializers.ModelSerializer):
tags = StringListField()
class Meta:
model = models.MyModel
def create(self, validated_data):
tags = validated_data.pop('tags')
instance = super(MyModelSerializer, self).create(validated_data)
instance.tags.set(*tags)
return instance
def update(self, instance, validated_data):
# looks same as create method
Problem
I'm trying to figure it out why when i submit my form, my tags are not saved in my db. Pretty new with the django-rest-framework and Django-taggit too, i think i'm doing something wrong :) First, before making my API with the rest-framework, i was using a generic view (CreateView and UpdateView) to register/validate my event. It was working fine but i decided to go further and try to build an API since i'm using Angularjs now. Now my model event is created but without my tag and i have some errors. I put some code and i'll describe my errors after. events/models.py ``` class Event(models.Model): [...] title = models.CharField(max_length=245, blank=False) description = models.TextField(max_length=750, null=True, blank=True) start = models.DateTimeField() end = models.DateTimeField() created_at = models.DateTimeField(editable=False) updated_at = models.DateTimeField(editable=False) slug = AutoSlugField(populate_from='title', unique=True, editable=False) expert = models.BooleanField(choices=MODE_EXPERT, default=0) home = models.BooleanField(choices=HOME, default=0) nb_participant = models.PositiveSmallIntegerField(default=1) price = models.PositiveSmallIntegerField(default=0) cancelled = models.BooleanField(default=0) user = models.ForeignKey(User, editable=False, related_name='author') address = models.ForeignKey('Address', editable=False, related_name='events') participants = models.ManyToManyField(User, related_name='participants', blank=True, editable=False, through='Participants') theme_category = models.ForeignKey('EventThemeCategory', unique=True, editable=False) tags = TaggableManager(blank=True) class Meta: db_table = 'event' def save(self, *args, **kwargs): if not self.pk: self.created_at = timezone.now() self.updated_at = timezone.now() super(Event, self).save(*args, **kwargs) [...] ``` i'm using the serializers.HyperlinkedModelSerializer. api/serializer.py ``` from taggit.models import Tag class TagListSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Tag fields = ('url', 'id', 'name') class EventSerializer(serializers.HyperlinkedModelSerializer): address = AddressSerializer() user = UserSerializer(required=False) tags = TagListSerializer(blank=True) class Meta: model = Event fields = ('url', 'id', 'title', 'description', 'start', 'end', 'created_at', 'updated_at', 'slug', 'expert','home', 'nb_participant', 'price', 'address', 'user', 'theme_category', 'tags') depth = 1 ``` api/views/tags_views.py ``` from rest_framework import generics from api.serializers import TagListSerializer from taggit.models import Tag class TagsListAPIView(generics.ListCreateAPIView): queryset = Tag.objects.all() model = Tag serializer_class = TagListSerializer class TagsDetailAPIView(generics.RetrieveUpdateDestroyAPIView): queryset = Tag.objects.all() model = Tag serializer_class = TagListSerializer ``` api/views/events_views.py ``` class EventListAPIView(generics.ListCreateAPIView): queryset = Event.objects.all() model = Event serializer_class = EventSerializer paginate_by = 100 def pre_save(self, obj): """ Set the object's owner, based on the incoming request. """ obj.user = self.request.user return super(EventListAPIView, self).pre_save(obj) ``` api/urls.py ``` url(r'^events/(?P<slug>[0-9a-zA-Z_-]+)/$', EventDetailAPIView.as_view(), name='event-detail'), ``` So first when i call /api/events/name-of-my-event the API send me the good resource with my tags on it. The GET method is working fine. I was thinking that rest-framework follow the query set. So if i can get the resource with with all my tags why when i use POST my tags are not register ? Actually i have two problems with the POST method: - first one if i send a tag which i have already created, he send me an error saying that the tag must be unique. I understand that, i don't want to create a new one, i just want it to be linked with my object. I don't have this problem when i use the generic view (it's done by magic :) and all is working fine) - Secondly, when i try to create a new tag, my new event is saved but without my tags. You can see the response received by angularjs for my tag... He send me the name of the tag but without id, url (hyperlinked). When i checked my db the tag has not been created. I think i have to make a custom get_queryset(self) in my tags_views but i'm not sure. I'll will continue to investigate. If someone have already to that and have some advise, i'll be very API. Thanks.