skip to Main Content

I have two models in my models.py file:

Flavour model:


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

    def __str__(self):
        return self.flavour_choice

and Product model:

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

I want to able to add flavours to the flavours table and then choose if they appear for particular products. How would I go about this? I know I could just add the flavours to the product but so many of the products I want to have, have the same flavours.

I need to be able to do this database side and not just programmatically so admin users can add flavours and products via a front-end product management page.

3

Answers


  1. Chosen as BEST ANSWER

    I went a different route in the end as I was overcomplicating the issue.

    I added a Variation model:

    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) 
    

    And now I can just use an if statement to call the flavours into the template in a for loop that iterates over all the flavours associated with the product in the product variation model.

    {% 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 %}
    

  2. You can use a ManyToMany relationship, where many flavours can be used for many different products.
    More explanation here : https://docs.djangoproject.com/en/4.1/topics/db/examples/many_to_many/

    Login or Signup to reply.
  3. First about your models and fields. You can use model choices at Flavour model, but it is not necessarily required. You can get rid of has_flavours and has_strength fields, use a model property and flavours and strengths relations to obtain the desired output.

    Also, as mentioned on the other answer, substitute the relation between Flavour and Product with a many-to-many relation in order to avoid duplicates in your database. Lastly, image_url is also not necessary, since you are using models.ImageField it is possible to access the image url via attribute e.g. instance.image.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
    

    A simple example, on how you would associate a flavour with a specific product:

    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)
    
    

    I would also consider the possibility of creating a model for brand = models.TextField() with a one-to-many relation.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search