簡體   English   中英

將 Django-taggit 與 django-rest-framework 一起使用,我無法保存我的標簽

[英]Using Django-taggit with django-rest-framework, i'm not able to save my tags

我想弄清楚為什么當我提交表單時,我的標簽沒有保存在我的數據庫中。 django-rest-framework 和 Django-taggit 也很新,我想我做錯了什么:)

首先,在使用 rest 框架制作我的 API 之前,我使用了一個通用視圖(CreateView 和 UpdateView)來注冊/驗證我的事件。 它工作正常,但我決定更進一步並嘗試構建 API,因為我現在正在使用 Angularjs。

現在我的模型事件已創建,但沒有我的標簽,我有一些錯誤。 我放了一些代碼,然后我會描述我的錯誤。

事件/模型.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)
    [...]

我正在使用 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'),

所以首先當我調用/api/events/name-of-my-event 時,API 會向我發送帶有我的標簽的好資源。 GET 方法工作正常。

我在想休息框架遵循查詢集。 因此,如果我可以獲得帶有所有標簽的資源,為什么當我使用 POST 時我的標簽沒有注冊?

實際上,我對 POST 方法有兩個問題:

  • 第一個,如果我發送一個我已經創建的標簽,他會向我發送一個錯誤,指出該標簽必須是唯一的。 我明白,我不想創建一個新的,我只是希望它與我的對象相關聯。 當我使用通用視圖時,我沒有這個問題(它是通過魔法完成的 :) 並且一切正常)
  • 其次,當我嘗試創建一個新標簽時,我的新事件被保存但沒有我的標簽。 您可以看到 angularjs 為我的標簽收到的響應......他向我發送了標簽的名稱,但沒有 id、url(超鏈接)。 當我檢查我的數據庫時,標簽尚未創建。接口響應

我想我必須在我的 tags_views 中創建一個自定義 get_queryset(self) 但我不確定。 我會繼續調查。 如果有人已經這樣做並有一些建議,我會非常 API。 謝謝。

遇到同樣的問題。 但我只想通過 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 類中的 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

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

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

我有一堆錯誤,但我找到了解決問題的方法。 也許不是最好的,因為我對所有這些都很陌生,但現在它有效。

我會嘗試描述我所有的錯誤,也許它會幫助某人。

首先,我的 angularjs 發送一個與查詢集完全匹配的 json

例如,對於我下面的模型事件,angularjs 發送到 API:

angularjs 控制器

現在讓我們從我所有的錯誤開始:

  • “同名標簽已經存在”

當我重新使用標簽時,出現此錯誤。 不知道為什么,因為使用沒有 API 的經典驗證,一切正常。

  • 使用新標簽也不會保存任何內容。

當我嘗試在我的事件事件模型上使用新標簽時,數據庫中沒有保存任何內容。 Angularjs 收到帶有標簽名稱但 ID 為 null 的響應(請參閱我原始問題的 pitcure)

  • “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)

但現在據說我有這個錯誤AttributeError: 'RelationsList' object has no attribute 'add' 實際上,很明顯,因為 obj.tags 是一個對象列表,而不是 TaggableManager 了。

所以我決定重新開始,而不是在“標簽”中發送我的標簽,而是在另一個自定義屬性“已標記”中發送我的標簽,以避免與 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)
  • “類型錯誤:不可散列的類型:'dict'”

現在,我發現我發送的標簽格式不正確。 我將它(在 angularjs 端)更改為發送這樣的數組 ['jazz','rock'] 而不是 [object, object]。 初學者的愚蠢錯誤。

現在奇跡發生了,angularjs 收到的響應很好:

角度響應確定

對不起我的英語不好。 我知道這可能不是最好的解決方案,當我找到另一個解決方案時,我會嘗試更新它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM