Используя Django-taggit с django-rest-framework, я не могу сохранить свои теги
Я пытаюсь понять, почему, когда я отправляю свою форму, мои теги не сохраняются в моей базе данных. Довольно плохо знаком с django-rest-framework и Django-taggit, я думаю, что я делаю что-то не так:)
Во-первых, прежде чем создавать свой API с помощью rest-framework, я использовал общее представление (CreateView и UpdateView) для регистрации / проверки моего события. Это работало нормально, но я решил пойти дальше и попытаться создать API, так как сейчас я использую Angularjs.
Теперь мое модельное событие создано, но без моего тега, и у меня есть некоторые ошибки. Я поставил код и опишу свои ошибки после.
События /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)
[...]
Я использую сериализаторы.HyperlinkedModelSerializer.
апи /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
апи / просмотров /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
апи / просмотров /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)
апи /urls.py
url(r'^events/(?P<slug>[0-9a-zA-Z_-]+)/$', EventDetailAPIView.as_view(), name='event-detail'),
Поэтому сначала, когда я вызываю /api/events/name-of-my-event, API отправляет мне хороший ресурс с моими тегами. Метод GET работает нормально.
Я думал, что остальные рамки следуют за набором запросов. Так что, если я могу получить ресурс со всеми моими тегами, почему, когда я использую POST, мои теги не регистрируются?
На самом деле у меня есть две проблемы с методом POST:
- Во-первых, если я отправляю тег, который я уже создал, он отправляет мне сообщение об ошибке, в котором говорится, что тег должен быть уникальным. Я понимаю, что я не хочу создавать новый, я просто хочу, чтобы он был связан с моим объектом. У меня нет этой проблемы, когда я использую общий вид (это сделано по волшебству:) и все работает нормально)
- Во-вторых, когда я пытаюсь создать новый тег, мое новое событие сохраняется, но без моих тегов. Вы можете увидеть ответ, полученный angularjs для моего тега... Он отправил мне имя тега, но без идентификатора, url (гиперссылка). Когда я проверил свою базу данных, тег не был создан.
Я думаю, что я должен сделать собственный get_queryset(self) в моем tags_views, но я не уверен. Я буду продолжать расследование. Если кто-то уже к этому и посоветует, я буду очень API. Благодарю.
3 ответа
Встретить тот же вопрос. Но я просто хочу сохранить список тегов непосредственно TaggableManager (без TagListSerializer и TagsListAPIView). Мое решение:
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)
Почтовые данные данных тегов будут ['tagA', 'tagB',...], TaggableManager будет обрабатывать их. Спасибо.
Для DRF>3.1 вам просто нужно переопределить создание и обновление в вашем классе ModelSerializer:
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
http://blog.pedesen.de/2013/07/06/Using-django-rest-framework-with-tagged-items-django-taggit/
С выпуском Django Rest Framework 3.0 код для TagListSerializer немного изменился. Сериализаторы serializer.WritableField устарели в пользу сериализаторов.Field для создания настраиваемых полей сериализатора, таких как этот. Ниже приведен исправленный код для Django Rest Framework 3.0.
class TagListSerializer(serializers.Field):
def to_internal_value(self, data):
if type(data) is not list:
raise ParseError("expected a list of data")
return data
def to_representation(self, obj):
if type(obj) is not list:
return [tag.name for tag in obj.all()]
return obj
Теперь я использую https://github.com/glemmaPaul/django-taggit-serializer библиотеку.
У меня была куча ошибок, но я нашел способ решить мою проблему. Может быть, не самый лучший, так как я довольно новичок во всем этом, но сейчас это работает.
Я постараюсь описать все мои ошибки, может быть, это кому-нибудь поможет.
Сначала мой angularjs отправляет json, который точно соответствует набору запросов
Так, например, с моими модельными событиями ниже, angularjs отправляет в API:
Теперь давайте начнем со всех моих ошибок:
- "Тег с таким именем уже существует"
При повторном использовании тега у меня появляется эта ошибка. Не знаю почему, потому что с классической проверкой без API все работает нормально.
- С новым тегом тоже ничего не сохраняется.
Когда я пытаюсь использовать новый тег в моей модели событий, в базе данных ничего не сохраняется. Angularjs получил ответ с именем тега, но с идентификатором NULL (см. Рисунок на моем исходном вопросе)
- "AttributeError: у объекта" RelationsList "нет атрибута" add ""
Теперь я пытаюсь думать, что для регистрации моих тегов мне нужно иметь уже созданный экземпляр события. Благодаря этому я смогу добавить свой тег, как описано в документе.
apple.tags.add ("красный", "зеленый", "фруктовый")
Поэтому я решил добавить post_save в мои 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)
def post_save(self, obj, created=False):
print 'tags', self.request.DATA
obj.tags.add(self.request.DATA['tags'])
return super(EventListAPIView, self).post_save(obj)
Но теперь, как говорится, у меня есть эта ошибка AttributeError: у объекта RelationsList нет атрибута add. На самом деле, это очевидно, поскольку obj.tags - это список объектов, а не TaggableManager.
Поэтому я решил начать все сначала и отправлять свои теги не в тегах, а в другом настраиваемом свойстве tagged, чтобы избежать конфликта с TaggableManager.
- "Ошибка типа: не подлежащий обработке тип: 'список'"
Новая ошибка:) Я нашел решение с помощью этого /questions/30113494/djangotaggit-nerazreshimyij-tip-spisok
def post_save(self, obj, created=False):
map(obj.tags.add, self.request.DATA['tagged'])
return super(EventListAPIView, self).post_save(obj)
- "TypeError: unhashable type: 'dict'"
Теперь я понял, что отправленные мной теги плохо отформатированы. Я изменил его (на стороне angularjs), чтобы он отправлял массив вроде этого ['jazz','rock'] вместо [object, object]. Глупая ошибка новичка.
Теперь волшебство случается, ответ angularjs хорош:
Извините за мой английский. Я знаю, что это может быть не лучшим решением, и я постараюсь обновить его, когда найду другое решение.