使用Django-taggit和django-rest-framework,我无法保存我的标签

Epo*_*pok 9 django django-taggit django-rest-framework

我想弄清楚为什么当我提交表单时,我的标签不会保存在我的数据库中.django-rest-framework和Django-taggit也很新,我觉得我做错了:)

首先,在使用rest-framework创建API之前,我使用通用视图(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)
    [...]
Run Code Online (Sandbox Code Playgroud)

我正在使用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
Run Code Online (Sandbox Code Playgroud)

API /视图/ 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
Run Code Online (Sandbox Code Playgroud)

API /视图/ 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)
Run Code Online (Sandbox Code Playgroud)

API/urls.py

    url(r'^events/(?P<slug>[0-9a-zA-Z_-]+)/$', EventDetailAPIView.as_view(), name='event-detail'),
Run Code Online (Sandbox Code Playgroud)

所以首先当我调用/ api/events/name-of-my-event时,API会向我发送包含我标签的好资源.GET方法运行正常.

我以为rest-framework遵循查询集.因此,如果我可以使用我的所有标签获取资源,为什么当我使用POST时,我的标签不会注册?

实际上我的POST方法有两个问题:

  • 首先,如果我发送了我已创建的标签,他会向我发送错误消息,说明标签必须是唯一的.我明白,我不想创建一个新的,我只是希望它与我的对象链接.当我使用通用视图时我没有这个问题(它由魔术完成:)并且一切正常
  • 其次,当我尝试创建一个新标签时,我的新事件被保存但没有我的标签.您可以看到angularjs收到的关于我的标签的响应......他发给我标签的名称,但没有id,url(超链接).当我检查我的数据库时,尚未创建标记. api回应

我想我必须在我的tags_views中制作一个自定义的get_queryset(self),但我不确定.我会继续调查.如果有人已经这样做并有一些建议,我会非常API.谢谢.

gze*_*one 10

遇到同样的问题.但我只是想通过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)
Run Code Online (Sandbox Code Playgroud)

标签数据的后期数据将是['tagA','tagB',...],TaggableManager将处理它.谢谢.

对于DRF> 3.1,您只需要覆盖ModelSerializer类中的create和update:

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
Run Code Online (Sandbox Code Playgroud)


auv*_*ipy 6

http://blog.pedesen.de/2013/07/06/Using-django-rest-framework-with-tagged-items-django-taggit/

随着 Django Rest Framework 3.0 的发布,TagListSerializer 的代码略有变化。serializers.WritableField 已贬值,有利于 serializers.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
Run Code Online (Sandbox Code Playgroud)

我现在使用https://github.com/glemmaPaul/django-taggit-serializer库。


Epo*_*pok 0

我遇到了很多错误,但我找到了解决问题的方法。也许不是最好的,因为我对这一切都很陌生,但现在它有效。

我会尝试描述我所有的错误也许会对某人有所帮助。

首先,我的 angularjs 发送一个与查询集完全匹配的 json

例如,对于下面的模型事件,AngularJS 发送到 API:

AngularJS 控制器

现在让我们从我的所有错误开始:

  • “具有该名称的标签已存在”

当我重新使用标签时,出现此错误。不知道为什么,因为使用没有 API 的经典验证,一切都工作正常。

  • 使用新标签也不会保存任何内容。

当我尝试在我的事件事件模型上使用新标签时,数据库中不会保存任何内容。Angularjs 收到了带有标签名称但 id 为 null 的响应(请参阅我原来问题的 piccure)

  • “AttributeError:‘RelationsList’对象没有属性‘add’”

现在我试图认为要注册我的标签,我需要已经创建一个事件实例。感谢这一点,我将能够在其上添加我的标签,就像文档中描述的那样。

apple.tags.add("红色", "绿色", "水果")

所以我决定在 events_views.py 中添加一个 post_save :

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)
Run Code Online (Sandbox Code Playgroud)

但现在正如所说,我有这个错误AttributeError: 'RelationsList' object has no attribute 'add'。实际上,这是显而易见的,因为 obj.tags 是一个对象列表,而不是 TaggableManager 了。

因此,我决定重新开始,并将我的标签发送到另一个自定义属性“tagged”中,而不是“tags”中,以避免与 TaggableManager 发生冲突。

  • “类型错误:不可散列的类型:'列表'”

新错误:)我找到了这个django-taggit-unhashable-type-list的解决方案

def post_save(self, obj, created=False):
    map(obj.tags.add, self.request.DATA['tagged'])
    return super(EventListAPIView, self).post_save(obj)
Run Code Online (Sandbox Code Playgroud)
  • “类型错误:不可散列的类型:'dict'”

现在,我发现我发送的标签格式不正确。我更改了它(在 angularjs 一侧)以发送这样的数组 ['jazz','rock'] 而不是 [object, object]。初学者的愚蠢错误。

现在奇迹发生了,AngularJS 收到的响应很好:

角度响应正常

对不起我的英语不好。我知道这可能不是最好的解决方案,当我找到另一个解决方案时,我会尝试更新它。