Django-Oscar - Modelling product classes with 2 main attributes each having different sub-attributes

190 views
Skip to first unread message

dbjax

unread,
Jul 16, 2022, 4:41:43 AM7/16/22
to django-oscar

The below question can also be found on Stack Overflow here

I am working on an online store for a jewellery brand using Django-Oscar. We have 6 product types (Rings, Earrings etc.), each of which has been defined as a Product Class.

All of these product classes have 2 common attribute types (Metal Type and Primary Gemstone Type) each of which take a collection of values. Depending on the values they take (eg. Diamond, Ruby etc. for Gemstone Type), they have different sub-attributes (eg. Carat, Clarity, Origin etc. for Diamond). Also, there are attributes (such as Dimensions, Weight etc.) which are common to all product classes.

To model these attributes, I am thinking of customising the Django-Oscar framework as follows:

  • Alter the AbstractProduct model to include two fields: metal and primary-gemstone such that every product instance has these two fields

  • To vary attributes depending on the selection for metal and primary-gemstone, create two models Metal and Gemstone comprising of the possible types of metals and gemstones as fields

  • Create a MetalAttribute model and a GemstoneAttribute model with metal, gemstone (PK from Metal and Gemstone models) and attribute-name (eg., for Diamond gemstone type, attribute names could be carat, cut etc.) fields

  • For the possible values of each attribute for each metal/ gemstone, we would need a model with product, metal-type, gemstone-type, metal-attribute, gemstone-attribute, other-attribute (attributes common to all product classes such as Dimensions, Weight etc. and created using the in-built AbstractProductAttribute model) and attribute-value fields.

Question

Is the above approach efficient? Is there another way to model the above attributes without customising the framework too much?

I thought of the following alternatives to avoid customising the framework but they don't seem to meet my requirements:

  • Change the definition of product classes to include type of product, metal and primary gemstone (eg. Rings-Gold-Sapphire) but we have 6 product types, at least 3 metal types and at least 5 primary gemstone types, implying at least 90 product classes which seems like a lot

  • Use the 6 product classes and the AbstractAttributeOptionGroup model to create a group of options for each metal or gemstone type (eg. Cut, Clarity, Origin for Diamond). But then, we would require these 'options' to hold values which, I think, is not possible within the framework.

  • Defining product classes using metal type and gemstone type (eg. different product classes for Gold, Silver, Diamond, Sapphire etc.) would not work since a given product instance would need to belong to 2 product classes, one for the metal type and the other for the gemstone type which is not currently not possible in the framework.

Joey Jurjens

unread,
Jul 16, 2022, 5:53:38 AM7/16/22
to django...@googlegroups.com
Hello dbajax,

Could you write out an example of the logic? I have difficulties understanding what you need exactly, but I think something like this could be achieved with the Entity Attribute Type, but I’m not sure as I don’t fully understand the logic based on the description you gave.

Let me know :)

Op za 16 jul. 2022 om 10:41 schreef dbjax <ishaan....@gmail.com>
--
https://github.com/django-oscar/django-oscar
http://django-oscar.readthedocs.org/en/latest/
---
You received this message because you are subscribed to the Google Groups "django-oscar" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-oscar...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/django-oscar/af938e5b-a665-4ec2-b5d6-a67d50a0e579n%40googlegroups.com.

Ishaan Narula

unread,
Jul 23, 2022, 11:09:09 AM7/23/22
to django...@googlegroups.com
Hi Joey

I managed to figure it out. However, I am now facing another issue. Could you please help me with it?


Eagerly hoping to solve it :)
Dbjax

You received this message because you are subscribed to a topic in the Google Groups "django-oscar" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/django-oscar/hDlotYXHFVo/unsubscribe.
To unsubscribe from this group and all its topics, send an email to django-oscar...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/django-oscar/CABWCobZLMF_nBi75DmdwT3Kn%3DpaMYAhPgceydj1Jp9to8ppqbg%40mail.gmail.com.

Joey Jurjens

unread,
Jul 23, 2022, 11:21:07 AM7/23/22
to django...@googlegroups.com
Hey dbajax,

You’re passing the metal as a form kwarg, not form argument. So you can either do kwargs.pop(“metal”, None) in the __init__ of the form (before calling super), you must then remove the “metal” as form init argument if you want to do it this way.

Or you can pass the metal type in the get_context_data method of the view. See this: 

That’s how you pass it to the form as argument instead of as a kwarg.

Let me know if you get it to work :)


Op za 23 jul. 2022 om 17:09 schreef Ishaan Narula <ishaan....@gmail.com>

Sebastian Jung

unread,
Jul 23, 2022, 11:29:02 AM7/23/22
to django...@googlegroups.com
Hallo,

why don't you use different product classes like gold,silver etc.pp. with different dynamic Attributes?

Regards

dbjax

unread,
Jul 24, 2022, 6:55:42 AM7/24/22
to django-oscar
Hi Joey

Thanks for the tips. :) 

So, I decided to take the second approach you propose, i.e. pass the metal type in the get_context_data method of the view in the formset_class of the items of the self.formsets dictionary (i.e. self.category_formset, self.image_formset, ...). I also altered those formset classes to include the 'metal' argument in their __init__ functions. Please see the attached files.

But I am still getting the same Traceback:
Exception Type: TypeError at /dashboard/catalogue/products/create/earrings/gold/diamond 
Exception Value: ProductForm.__init__() missing 1 required positional argument: 'metal'

Pls let me know if you can find the cause of the Traceback.

Thanks
dbjax
models.py.pdf
formsets.py.pdf
views.py.pdf
forms.py.pdf

dbjax

unread,
Jul 24, 2022, 7:00:04 AM7/24/22
to django-oscar
Hi

Defining product classes using metal type and gemstone type (eg. different product classes for Gold, Silver, Diamond, Ruby etc.) would not work since, in that case, a given product instance would need to belong to 2 product classes, one for the metal type (gold, silver, etc.) and the other for the gemstone type (diamond etc.) which I guess is currently not possible in the framework

Joey Jurjens

unread,
Jul 24, 2022, 7:10:48 AM7/24/22
to django...@googlegroups.com
Hi dbajax,

I think you’re getting this error because you’re calling super.get_context_data. This will still try to create forms without the metal argument ( as that’s what it originally does )

So I think you’ll have to override the entire get_context_data without calling super.

Then it should work I think. If not, could you paste the entire traceback?

Let me know!

Op zo 24 jul. 2022 om 12:55 schreef dbjax <ishaan....@gmail.com>

dbjax

unread,
Jul 25, 2022, 3:30:11 AM7/25/22
to django-oscar
Hi Joey

Thanks for pointing that out. I have now removed super.get_context_data and set ctx to be an empty dictionary. I have also done the same for the get_form_kwargs method (see attached views file). After doing this, I am still getting the same traceback (attached).

In the traceback, I now notice that Django is executing get_context_data method in ProductCreateUpdateView from oscar's original views.py file, and not the ProductCreateUpdateView from my views.py file from the forked dashboard app (see green highlighted line in file). Could that be the reason for this traceback?

If so, I don't know why Django is not reading my views.py file. I think the dashboard app has been correctly forked since the other views which I customised are working fine.

Let me know what you think
traceback.pdf
views_v3.py.pdf

dbjax

unread,
Jul 25, 2022, 10:42:43 AM7/25/22
to django-oscar
Hi Joey

I figured out the above. The issue was in the apps.py file where I forgot to reference the concerned URL to my customised ProductCreateUpdateView. That was silly of me. But thanks for all your advice throughout. I have attached the code here.

I have now customised the product creation/ edit page to reflect attributes of the chosen metal type (see screenshots.pdf). But when I click on the 'Save' button or the 'Save and continue editing' button, nothing happens. :/ Not sure what's wrong now, and there's no traceback to offer some hint. 

My intuition is that it has something to do with this line (https://github.com/django-oscar/django-oscar/blob/master/src/oscar/templates/oscar/dashboard/catalogue/product_update.html#L34) in the product_update.html template and the fact that my 'catalogue-product-create' URL now looks like this 'products/create/<slug:product_class_slug>/<slug:metal_slug>/<slug:gemstone_slug>'.

I thought I finally solved this but am stuck again hahah. Anyways, let me know if you have thoughts on the above.

Thanks

forms.py.pdf
views_v4.py.pdf
models.py.pdf
apps.py.pdf
screenshots.pdf

Joey Jurjens

unread,
Jul 26, 2022, 9:35:16 AM7/26/22
to django...@googlegroups.com
Hey dbjax,

That sounds quite strange. With "nothing happens", do you mean you're clicking the button but it's not submitting the form, or do you mean it's not saving the data?

If it's the first, then you could try removing the "{% if request.GET.urlencode %}?{{ request.GET.urlencode }}{% endif %}" from the template and see what happens.
But honestly I don't think that's it, as that's for getting the query string and your new url has nothing to do with that. You could also try to submit the form through the javascript console and see what happens?

Let me know!

Op ma 25 jul. 2022 om 16:42 schreef dbjax <ishaan....@gmail.com>:

Joey Jurjens

unread,
Jul 26, 2022, 9:45:50 AM7/26/22
to django-oscar
Little follow up on this, it might be that html5 form validation sees one form as invalid? Because the product create template has tabs for each form, errors might not be obvious and you'll have to manually check the tabs. Though, I thought I fixed this a few months ago that the tabs show an error icon.

You can also add "novalidate" on the form to disable html5 validation, if the form then does submit, then a form is invalid.

Op dinsdag 26 juli 2022 om 15:35:16 UTC+2 schreef Joey Jurjens:

dbjax

unread,
Jul 27, 2022, 12:46:19 PM7/27/22
to django-oscar
Hi Joey

Both of your tips were very helpful. On the JavaScript Console, I got the error (pls see attached screenshots):
An invalid form control with name='attr_gold_colour' is not focusable

When I googled it, I came across this StackOverflow post, and after reading it, I removed the required=True condition from the metal attributes. I then tried to click submit, the form submitted. 

I also tried to add 'novalidate' to the form but then kept required=True (which is something I would prefer to have) for the various metal attributes. With this approach too, the form submission was successful. Also, when following this approach, I get the desired error messages when I leave some required fields blank (see attached screenshot). So, I will pursue this instead of setting required=False.

Big thank you once again :)
Screenshot 2022-07-27 at 22.03.52.png
Screenshot 2022-07-27 at 17.56.54.png

Joey Jurjens

unread,
Jul 27, 2022, 4:31:59 PM7/27/22
to django...@googlegroups.com
Hi dbajax,

Glad you got it to work. That focusable error is because of the tabs, not being able to display the errors.

The tab should show a icon error though, but it mush have some specific class in order for our JS to pick it up; 

So you might want to check that :) 

But at least it works now, awesome!

Op wo 27 jul. 2022 om 18:46 schreef dbjax <ishaan....@gmail.com>
Reply all
Reply to author
Forward
0 new messages