Django: Установка значения "многие-ко-многим" для модели

Я пытаюсь протестировать свои модели Project, Category и Tag. Я столкнулся с проблемой при попытке добавить теги к моей модели проекта.

Он не позволяет мне сделать это в самой модели Project, например,

self.project = Project.objects.create(
...
tags=Tag.objects.create("HTML5"),
)

В документации Django предлагается сделать это следующим образом. Однако я не могу "добавить" Tag без сохранения модели, и я не могу сохранить модель без добавления Tag

Тесты

class ProjectTests(TestCase):
    def setUp(self):

        self.tag = Tag.objects.create(name="HTML5")

        self.project = Project(
            title="Oaks on Main Shopping Center",
            url="www.oaksonmain.co.za",
            image=SimpleUploadedFile(
                name="test-image.jpg",
                content=open(
                    "static\\images\\test_images\\florian-olivo-4hbJ-eymZ1o-unsplash (1).jpg", "rb"
                ).read(),
                content_type="image/jpeg",
            ),
            description="Beautiful website created for Oaks on Main Shopping Center in Knysna!",
            category=Category.objects.create(name="Website"),
        )

        self.project.save() <- Problem here
        self.project.tags.add(self.tag)  <- Problem here
        

    def test_project_model(self):
        self.assertEqual(f"{self.project.title}", "Oaks on Main Shopping Center")
        self.assertEqual(f"{self.project.url}", "www.oaksonmain.co.za")
        self.assertEqual(
            f"{self.project.description}",
            "Beautiful website created for Oaks on Main Shopping Center in Knysna!",
        )
        self.assertEqual(self.tags.count(), 3)
        self.assertEqual(self.category.count(), 1)
        self.assertEqual(self.image.count(), 1)

    def test_project_listview(self):
        resp = self.client.get(reverse("index"))
        self.assertEqual(resp.status_code, 200)
        self.assertContains(resp, self.project.title)
        self.assertTemplateUsed(resp, "page/index.html")

Модели

class Project(models.Model):
    class Meta:
        ordering = ["-id"]  # Always show latest projects first
        verbose_name_plural = "Projects"

    title = models.CharField(max_length=50)
    url = models.URLField()
    image = models.ImageField(upload_to=f"{title}/")
    description = models.TextField()
    category = models.ForeignKey("Category", on_delete=models.PROTECT, related_name="categories")
    tags = models.ManyToManyField("Tag", verbose_name="tags")

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse("index")


class Category(models.Model):
    class Meta:
        ordering = ["name"]
        verbose_name_plural = "Categories"

    name = models.CharField(max_length=20)

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse("index")


class Tag(models.Model):
    class Meta:
        ordering = ["name"]
        verbose_name_plural = "Tags"

    name = models.CharField(max_length=10)

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse("index")

Вы можете сначала сохранить тег или объект модели, а затем позже связать тег как (один из) tags этого Project, так:

html5_tag, __ = Tag.objects.get_or_create(name='HTML5')
self.project = Project.objects.create(
    # no tags=…
)
self.project.tags.add(html5_tag)

Ваш ImageField также должен работать с callable для upload_to=… параметра, так:

class Project(models.Model):
    # …
    def upload_image(self, filename):
        return f'{self.title}/{filename}'
    image = models.ImageField(upload_to=upload_image)
    # …
Вернуться на верх