postgresql 仅当选项存在于两个Django模型中时返回选项列表

ki0zmccv  于 2023-03-01  发布在  PostgreSQL
关注(0)|答案(3)|浏览(113)

我的www.example.com文件中有两个模型models.py:
风味型号:

class Flavour(models.Model):
    flavour_choice = models.CharField(null=True, blank=True, max_length=254)

    def __str__(self):
        return self.flavour_choice

产品型号:

class Product(models.Model):
    category = models.ForeignKey(
        'Category', null=True, blank=True, on_delete=models.SET_NULL
        )
    slug = models.SlugField()
    sku = models.CharField(max_length=254, null=True, blank=True)
    name = models.CharField(max_length=254)
    brand = models.TextField()
    has_flavours = models.BooleanField(default=False, null=True, blank=True)
    flavours = models.ForeignKey(
        'Flavour', null=True, blank=True, on_delete=models.SET_NULL
        )
    has_strength = models.BooleanField(default=False, null=True, blank=True)
    strength = models.ForeignKey(
        'Strength', null=True, blank=True, on_delete=models.SET_NULL
        )
    description = models.TextField()
    price = models.DecimalField(max_digits=6, decimal_places=2)
    rating = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
    image_url = models.URLField(max_length=1024, null=True, blank=True)
    image = models.ImageField(null=True, blank=True)
    display_home = models.BooleanField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ('-created_at',)

    def __str__(self):
        return self.name

我希望能够添加口味到口味表,然后选择它们是否出现在特定的产品中。我该怎么做呢?我知道我可以只添加口味到产品中,但我想拥有的许多产品都有相同的口味。
我需要能够做到这一点的数据库方面,而不仅仅是编程,使管理员用户可以添加口味和产品通过前端产品管理页面。

cuxqih21

cuxqih211#

您可以使用ManyToMany关系,在这种关系中,多种口味可用于多种不同的产品。https://docs.djangoproject.com/en/4.1/topics/db/examples/many_to_many/

alen0pnh

alen0pnh2#

首先关于你的模型和字段。你可以在Flavour模型中使用模型选择,但这不是必须的。你可以去掉has_flavourshas_strength字段,使用模型属性和flavoursstrengths关系来获得想要的输出。
另外,正如在另一个答案中提到的,用多对多关系替换FlavourProduct之间的关系,以避免在数据库中重复。最后,image_url也是不必要的,因为您使用的是models.ImageField,可以通过属性(例如instance.image.url)访问图像url。
models.py

class Product(models.Model):
    category = models.ForeignKey(
        'Category', null=True, blank=True, on_delete=models.SET_NULL
        )
    slug = models.SlugField()
    sku = models.CharField(max_length=254, null=True, blank=True)
    name = models.CharField(max_length=254)
    brand = models.TextField()
    flavours = models.ManyToManyField('Flavour')
    strength = models.ForeignKey(
        'Strength', null=True, blank=True, on_delete=models.SET_NULL
        )
    description = models.TextField()
    price = models.DecimalField(max_digits=6, decimal_places=2)
    rating = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
    image = models.ImageField(null=True, blank=True)
    display_home = models.BooleanField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ('-created_at',)

    def __str__(self):
        return self.name

    @property
    def has_flavours(self):
        return True if self.flavours.count() > 0 else False

    @property
    def has_strength(self):
        return True if self.strength.count() > 0 else False

一个简单的例子,关于你如何将一种口味与一种特定的产品联系起来:
tests.py

class ProductTestCase(TestCase):
    def setUp(self):
        self.flv1 = Flavour.objects.create(flavour_choice='Flavour One')
        self.flv2 = Flavour.objects.create(flavour_choice='Flavour Three')

        self.product = Product.objects.create(
            slug='product-one', name='Product One',
            brand='Brand', description='Product Description',
            price=decimal.Decimal(100), display_home=True
        )

        self.another_product = Product.objects.create(
            slug='another-product', name='Another Product',
            brand='Brand', description='Another Product Description',
            price=decimal.Decimal(100), display_home=True
        )

    def test_product_with_specific_flavour(self):
        self.product.flavours.add(self.flv1)
        self.product.flavours.add(self.flv2)
        self.another_product.flavours.add(self.flv2)

        queryset = Product.objects.filter(flavours=self.flv2)
        # queryset contains both products
        self.assertEqual(queryset.count(), 2)

        queryset = Product.objects.filter(flavours=self.flv1)
        # queryset contains only 'Product One'
        self.assertEqual(queryset.count(), 1)
        self.assertEqual(queryset[0], self.product)

    def test_product_flavour_property(self):
        # Associate flavour with poduct one
        self.product.flavours.add(self.flv1)

        # Product One has flavour and another_product does not.
        self.assertEqual(self.product.has_flavours, True)
        self.assertNotEqual(self.another_product.has_flavours, True)

我还会考虑使用one-to-many关系为brand = models.TextField()创建模型的可能性。

mf98qq94

mf98qq943#

最后我走了一条不同的路线,因为我把问题搞得过于复杂。
我添加了一个变体模型:

class Variation(models.Model): product = models.ForeignKey( 'Product', null=True, blank=True, on_delete=models.SET_NULL ) flavour = models.CharField(max_length=254, null=True, blank=True) strength = models.CharField(max_length=254, null=True, blank=True)

现在我可以使用if语句在for循环中调用模板中的口味,循环迭代产品变体模型中与产品相关的所有口味。

{% if product.has_flavours %}
                        <div class="col-12">
                            <p><strong>Flavour:</strong></p>
                            <select class="form-control rounded-0 w-50" name="product_flavour" id='id_product_flavour'>
                                {% for variation in variations %}
                                <option value="{{ variation.flavour }}">{{ variation.flavour }}</option>
                                {% endfor %}
                            </select>
                        </div>
                        {% endif %}

相关问题