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)
# …