Customizing the widget for the ManyToMany field for a model that has a circular ForeignKey for itself

I have two category models and products.

  • A product may have several categories.
  • A category can have several products.
  • Categories have a circular foreign key for themselves.
  • not all categories have the same level of depth

Example:

  • Category A
    • Category A_1
    • category A_2
      • Category A_2_1
  • Category B
  • Category C
    • Category C_1

models.py

 class Product: categories = models.ManyToManyField(Category) name = models.CharField(max_length=255) class Category: categories = models.ForeignKey(self) name = models.CharField(max_length=255) 

As a form, I use ModelForm:

 class ProductForm(ModelForm): class Meta: model = Product fields = ['categories', 'name', 'short_description', 'description'] widgets = { 'categories': MyWidget, } 

What I want to achieve:

I want to implement conditional selection (narrow options). When creating a product form:

  • Only the top parent categories are available (level 0 A, B, C)
  • The user selects the parent category. If the parent has children, a new β€œChoice” field appears with his children (category level 1 A1, C1)
  • The user selects level 1 category (A1, C1). If the parent has children, a new selection box appears with his children (level 2 A2)

    • The process is repeated until the children are available (recursive), the user selects the smallest category in the tree
    • A new button is available for the user to add additional categories and restart the 1-3 process.
    • I want to make a choice, add a new option using JavaScript
    • In the Submit form, I want to send only the latest categories of children

Parameters I thought:

  • Change the default default fields ManyToMany - it looks like there are no good hooks and / or inheritance
  • Use a custom ManytoMany field device (such as Charfield) - more complex on a clean, shape-preserving
  • Edit / Inherit widget. My problems are how to send data to the default field for sending and get / show it when editing

Practically, let's say I have 7 Select Boxes, with values ​​for each:

  • Parent1-> Child11-> Child111
  • Parent2-> Child21
  • Parent3-> Child31-> Child311

How to inform Django about sending a browser (with other data) to send ManyToMany to the last Child in all three of them

I can collect them using Javascript, but I have to say that Django gets this data, this is what you need.

I need help with the quality of the code and an indication of how to do this.

enter image description here

+5
source share
1 answer

Django allows you to define the model you are referencing in the ForeignKey or ManyToManyField as '<app_name>.<model_name>' instead of importing the model and directly assigning it. This solves a lot of problems, especially circular imports.


Assuming you have categories applications with the Category model and products with the Product model, this is:

products/models.py :

 class Product: categories = models.ManyToManyField(Category) name = models.CharField(max_length=255) 

categories/models.py :

 class Category: categories = models.ManyToManyField(self) name = models.CharField(max_length=255) 

can directly translate to what you need:

products/models.py :

 class Product: categories = models.ManyToManyField('categories.Category') name = models.CharField(max_length=255) 

categories/models.py :

 class Category: categories = models.ManyToManyField('self') name = models.CharField(max_length=255) 

However, you can have as many categories as you want:

 category_one = Category.create(name='Category one') category_two = Category.create(name='Category two') category_three = Category.create(name='Category three') category_three.categories.add(category_one, category_two) some_product = Product.create(name='Test Product') some_product.categories.add(category_three) 

( ManyToManyField Docs )


It is also important to note that with any Python class, self not the class itself - it is an instance. Therefore, you cannot reference it outside the instance method, there is a good explanation of the reasons here . The only reason the 'self' line works is because Django translates this to the class it is in, categories.Category - so it would be nice to replace self with 'categories.Category' to be explicit.

+2
source

Source: https://habr.com/ru/post/1273315/


All Articles