AttributeError: 'QuerySet' object has no attribute 'tags' when trying to save quotes and tags with ClusterableModel and ClusterTaggableManager

AttributeError: 'QuerySet' object has no attribute 'tags' when trying to save quotes and tags with ClusterableModel and ClusterTaggableManager

我正在尝试使用 python manage.py shell 将 csv 文件中的一些引号保存到我的 django 模型中,因为我无法使用 django-import-export 来执行此操作。我在 Tags imported but relationship not imported with django-import-export 上询问过,但没有人回答,谷歌搜索后我找不到更多可以尝试的东西。

在阅读了此处关于 Django 查询集和标记的文档和之前的回答后,我设法保存了每个引用的大部分内容,但没有保存标记。查询集没有returntags字段,导致AttributeError。请参阅下面的 shell 输出 q: <QuerySet...

我的标签模型遵循 https://docs.wagtail.io/en/stable/reference/pages/model_recipes.html#custom-tag-models。 在 django-admin 中,关系正在发挥作用。 所以拼图的缺失部分是保存标签。 我应该使用什么查询来定位标签字段或者我应该使用什么方法来保存标签?

#我正在尝试 运行 在 python manage.py shell

中的脚本
import csv
from quotes.models import Quote, TaggedQuote

with open("test_import1.csv", "r", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for row in reader:
        if row["book"]:
            item = Quote(
                text=row["text"],
                author=row["author"],
                source_url=row["url"],
                book=row["book"],
            )
        elif not row["book"]:
            item = Quote(text=row["text"], author=row["author"], source_url=row["url"])
        item.save()
        for each in row["tags"].split(", "):
            each = str(each).strip("'")
            q = Quote.objects.filter(text__exact=row["text"].strip('"')).values()
            print(f"each: {each}")
            print(f"q: {q}")
            q.tags.add(each)

Shell输出

each: attributednosource
q: <QuerySet [{'id': 56, 'text': "I'm selfish, impatient and a little insecure. I make mistakes, I am out of control and at times hard to handle. But if you can't handle me at my worst, then you sure as hell don't deserve me at my best.", 'author': 'Marilyn Monroe', 'book': '', 'book_url': '', 'source_url': 'https://www.goodreads.com/quotes/tag/love', 'user_id': None, 'imported': True, 'created': datetime.datetime(2021, 9, 1, 9, 53, 25, 224023, tzinfo=<UTC>), 'updated': datetime.datetime(2021, 9, 1, 9, 53, 25, 224084, tzinfo=<UTC>), 'active': True, 'inactive_message': ''}]>
Traceback (most recent call last):
  File "<console>", line 26, in <module>
AttributeError: 'QuerySet' object has no attribute 'tags'

models.py

from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import models
from modelcluster.fields import ParentalKey
from modelcluster.models import ClusterableModel
from modelcluster.contrib.taggit import ClusterTaggableManager
from taggit.models import TagBase, ItemBase
from wagtail.snippets.models import register_snippet

from ckeditor.fields import RichTextField


import sys

sys.path.append("..")  # Adds higher directory to python modules path.


@register_snippet
class QuoteTag(TagBase):
    class Meta:
        verbose_name = "Quote Tag"
        verbose_name_plural = "Quote Tags"


class TaggedQuote(ItemBase):
    tag = models.ForeignKey(
        QuoteTag, related_name="tagged_quotes", on_delete=models.CASCADE
    )
    content_object = ParentalKey(
        to="Quote", related_name="tagged_items", on_delete=models.CASCADE
    )


def get_sentinel_user():
    return get_user_model().objects.get_or_create(username="deleted_user")[0]


# DO NOT use set_admin(). Default cannot be a query because it will be executed before migration and cause error.
# def set_admin():
#     return get_user_model().objects.get(pk=1).id


class Quote(ClusterableModel):
    text = RichTextField(
        config_name="awesome_ckeditor", help_text="You must enter some quote content"
    )
    author = models.CharField(
        max_length=300, blank=True, help_text="Person who said/wrote this quote"
    )
    book = models.CharField(
        max_length=300,
        blank=True,
        help_text="If the quote is from a book, enter book title here",
    )
    book_url = models.URLField(max_length=300, blank=True)
    tags = ClusterTaggableManager(through="TaggedQuote", blank=True)
    source_url = models.URLField(
        max_length=300,
        blank=True,
        help_text="If applicable: Paste the url link where you found the quote here",
    )
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        blank=True,
        null=True,
        on_delete=models.SET(get_sentinel_user),
    )
    # DO NOT use default=set_admin(). Default cannot be a query because it will be executed before migration and cause  error.
    # user = models.ForeignKey(CustomUser, blank=True, null=True, default=set_admin(), on_delete=models.SET(get_sentinel_user),)
    imported = models.BooleanField(default=True)
    created = models.DateTimeField(auto_now_add=True, editable=False)
    updated = models.DateTimeField(auto_now=True, editable=False)
    active = models.BooleanField(
        default=True, help_text="Allows moderator to unpublish quote if set to false"
    )  # Allows moderators to hide offensive posts
    inactive_message = models.TextField(
        max_length=500,
        blank=True,
        help_text="Enter the reason for setting this quote to inactive",
    )

    def __str__(self):
        """String repr of this class."""
        return f"{self.text} - {self.author}"

    class Meta:

        verbose_name = "Quote"
        verbose_name_plural = "Quotes"

test_import1.csv

,text,author,tags,url,book
0,"I'm selfish, impatient and a little insecure. I make mistakes, I am out of control and at times hard to handle. But if you can't handle me at my worst, then you sure as hell don't deserve me at my best.",Marilyn Monroe,"attributednosource, best, life, love, mistakes, outofcontrol, truth, worst",https://www.goodreads.com/quotes/tag/love,
1,"You've gotta dance like there's nobody watching, Love like you'll never be hurt, Sing like there's nobody listening, And live like it's heaven on earth.",William W. Purkey,"dance, heaven, hurt, inspirational, life, love, sing",https://www.goodreads.com/quotes/tag/love,
2,You know you're in love when you can't fall asleep because reality is finally better than your dreams.,Dr. Seuss,"attributednosource, dreams, love, reality, sleep",https://www.goodreads.com/quotes/tag/love,
3,A friend is someone who knows all about you and still loves you.,Elbert Hubbard,"friend, friendship, knowledge, love",https://www.goodreads.com/quotes/tag/love,
4,Darkness cannot drive out darkness: only light can do that. Hate cannot drive out hate: only love can do that.,Martin Luther King Jr.,"darkness, driveout, hate, inspirational, light, love, peace",https://www.goodreads.com/quotes/tag/love,A Testament of Hope: The Essential Writings and Speeches
5,We accept the love we think we deserve.,Stephen Chbosky,"inspirational, love",https://www.goodreads.com/quotes/tag/love,The Perks of Being a Wallflower

使用 Quote.objects.get(text__exact=row["text"].strip('"')) 有效。我预计 Quote.objects.filter(text__exact=row["text"].strip('"')) 到 return 1 个结果并认为它应该有效但我错了。

我在 python manage.py shell 中对此进行了测试并且有效。

import csv
from quotes.models import Quote

with open("test_import1.csv", "r", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for row in reader:
        # Check if quote exists
        if Quote.objects.filter(text__exact=row["text"].strip('"')):
            print(f"Not added because it already exists: {row['text']}")
        else:
            if row["book"]:
                item = Quote(
                    text=row["text"],
                    author=row["author"],
                    source_url=row["url"],
                    book=row["book"],
                )
            elif not row["book"]:
                item = Quote(text=row["text"], author=row["author"], source_url=row["url"])
            item.save()
            for each in row["tags"].split(", "):
                each = str(each).strip("'")
                q = Quote.objects.get(text__exact=row["text"].strip('"'))
                q.tags.add(each)
                q.save()

q.tags.add(each)中,q是对象列表,所以不能直接使用tag

遍历每个引用对象:

for q in Quote.objects.filter(text__exact=row["text"].strip('"')):
    q.tags.add(each)

或通过get获得一个对象:

q = Quote.objects.get(text__exact=row["text"].strip('"'))
q.tags.add(each)

或过滤 return 个对象,例如使用 first:

q = Quote.objects.filter(text__exact=row["text"].strip('"')).first()
q.tags.add(each)