0

I need to create two models from a single template. Creating Product model is fine. The Product model has the ManyToOne relation with ProductVariant. But I got problem while creating ProductVariant model. request.POST.getlist('names') this gives me the result like this ['name1','name2] and the same goes for all. I want to create ProductVariant object with each values. How can I do this ? Also I think there is a problem while stroing a HStoreField. request.POST.getlist('attributes') gives the value like this ['a:b','x:z'] so I converted it into dictionary(but not sure it works).

UPDATE:

What I want is attributes, names ... all will have the same number of items in the list. For example if the name is ['a','b','c'] then weight will also have 3 values in the list [12,15,23] like this. I want to create ProductVariant object 3 times since every list will have 3 items in the list. The first object will have field values from the list first item which is name=a,weight=12.. and for the second object values will be name=b, weight=15 like this.

How will it be possible? Or I should change the logic ? Any suggestions ? models

class ProductVariant(models.Model):
    name = models.CharField(max_length=255, blank=False, null=False)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    attributes = HStoreField()
    price = models.FloatField(blank=False, null=False, default=0.0) 

views

        product = product_form.save()           
        attributes = request.POST.getlist('attributes')
        names = request.POST.getlist('name')
        up = request.POST.getlist('price')
        weight = request.POST.getlist('weight')
        print(names, 'names')
        # converting attributes into the dictionary for the HStore field
        for attribute in attributes:
            attributes_dict = {}
            key, value = attribute.split(':')
            attributes_dict[key] = value
        
       ProductVariant.objects.create(name=name,...) # for each value I want to create this.

4 Answers 4

2

Answer for update:

names = ['a', 'b', 'c']
weights = [12, 15, 23]

params = zip(names, weights)
products = [ProductVariant(name=param[0], weight=param[1]) for param in params]
ProductVariant.objects.bulk_create(products)
Sign up to request clarification or add additional context in comments.

1 Comment

please refer to posting 1 answer if you have multiple methods instead of posting multiple answers
1

I disagree with this approach, but if you really want to do it this way, ziping would be the way as @forkcs pointed out.

I would use Django to help me as much as possible, before i get there, please make this change. float != money

class ProductVariant(models.Model):
    name = models.CharField(max_length=255, blank=False, null=False)
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    attributes = HStoreField()
    price = models.DecimalField(blank=False, null=False, default=0, max_digits=6, decimal_places=2)

Once thats done, the form should look like this:

class ProductVariantForm(forms.ModelForm):
    class Meta:
        fields = ('name', 'product', 'attributes', 'price')
        model = ProductVariant


ProductVariantFormSet = formset_factory(ProductVariantForm)

Note that I don't have to parse/clean/format attributes? Thats because Django did it for me ;)

And you can use it as follow IF you raname your fields and not use the same name multiple times: (instead of all your fields being called "attributes", you call them "form-X-attributes" where X is the number 0-infinity, example)

    product = product_form.save()    
    formset = ProductVariantFormSet(data=request.POST)
    if formset.is_valid():
        instances = []
        for form in formset:
            if form.is_valid():  # this could probably be removed
                instances.append(form.save())

For extra credit you can also do: (it shouldn't really matter)

    product = product_form.save()    
    formset = ProductVariantFormSet(data=request.POST)
    if formset.is_valid():
        instances = []
        for form in formset:
            if form.is_valid():  # this could probably be removed
                instances.append(form.save(save=False))
        ProductVariant.objects.bulk_create(instances)

What do you gain? STANDARDS!!! AND compartmentalization! Everyone that knows Django knows what you did. All your clean logic will be placed in the right place (the form), and you'll be less error prone.

Ps. i wrote tests for you. https://gist.github.com/kingbuzzman/937a9d207bd937d1b2bb22249ae6bdb2#file-formset_example-py-L142 If you want more information on my approach, see the docs https://docs.djangoproject.com/en/3.1/topics/forms/formsets/

3 Comments

Will there be any issues with zip since it just solved my issue. Anyway thanks for the information and will try this way too.
Yes there is an issue, you have to hand roll way too much, take care of clean up, error checking... etc. Other than that, there is no issue 😜
@helloworld i suggest you look at youtu.be/Uwuv05aZ6ug?t=361 -- the first piece of the talk is whats relevant to what i'm suggesting -- it's about Miller's law (the whole talk is great)
0

As for attributes, it could be reduced to one line like this:

attributes_dict = dict(map(lambda x: x.split(':'), attributes))

To create multiple objects you should either iterate and create one object at a time or use bulk_create:

for name in names:
    ProductVariant.objects.create(name=name,...)

Or

ProductVariant.objects.bulk_create([ProductVariant(name=name) for name in names])

6 Comments

I have to use for loop not only for name but also for other values.
With bulk create can I use other loops after for name in names also ?
Like this? ProductVariant.objects.bulk_create([ProductVariant(name=name, price=p) for name in names for p in up])
Then state your question better. You can use whatever you want in for loop, no one and nothing holds you. Please, read the docs: docs.djangoproject.com/en/3.1/ref/models/querysets/#bulk-create
Then once again, describe you problem better. Update the post, state the problem, show logs, explain desired result.
|
0

Best practice for this is using bulk_create method.

product_variants = [ProductVariant(name=name) for name in names]
ProductVariant.objects.bulk_create(product_variants)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.