R
R
Ruslan Gilfanov2017-05-31 11:23:40
Django
Ruslan Gilfanov, 2017-05-31 11:23:40

How to override possible ManyToManyField values ​​in InlineModelAdmin given the value of another inline form field?

1. Products belong to categories
2. Categories have filters with possible values
​​3. Products have attributes with the same possible values
​​How can I override the inline ManyToManyField fields in product editing so that the selection of attribute values ​​matches the set of filter values?
The formfield_for_manytomany method of the admin.TabularInline class is called somewhere in the loop and receives as an argument only a single field (db_field) of a single inline form.
To override the selection, I need to have access to the neighboring field (filter) of the inline form and its value set (filter.variant_set) in this method.
How can I pass the values ​​of neighboring fields of the same inline form to this method? Which method to override?
1. models.py (abbreviated)

class Category(MPTTModel):  # Категория
    name = models.CharField('название', max_length=64)


class Product(models.Model):  # Товар
    category = TreeForeignKey(Category, models.CASCADE, verbose_name='категория')

    def save(self, *args, **kwargs):
        # тут кастомная логика с авто-удалением/добавлением атрибутов товарам


class VariantFilter(models.Model):  # Фильтр категории
    name = models.CharField('имя фильтра', max_length=64)
    category = TreeForeignKey(Category, models.CASCADE, verbose_name='категория')

    def save(self, *args, **kwargs):
        # тут кастомная логика с автодобавлением атрибутов товарам


class Variant(models.Model):  # Значения атрибутов товаров
    value = models.CharField('значение', max_length=64)
    filter = models.ForeignKey(VariantFilter, models.CASCADE)


class VariantAttribute(models.Model): #  Атрибут товара
    product = models.ForeignKey(Product, models.CASCADE, verbose_name='товар')
    filter = models.ForeignKey(VariantFilter, models.CASCADE, verbose_name='атрибут')
    values = models.ManyToManyField(Variant, blank=True, verbose_name='значения')

    class Meta:
        unique_together = (('product', 'filter'),)

2. admin.py (abbreviated)
# Инлайн-поля со значениями фильтров
class VariantInline(admin.TabularInline):
    model = Variant
    extra = 1


# Редактирование фильтра
@admin.register(VariantFilter)
class VariantFilterAdmin(admin.ModelAdmin):
    inlines = [VariantInline]


# Инлайн-поля с атрибутами товаров
class VariantAttributeInline(admin.TabularInline):
    model = VariantAttribute
    max_num = 0
    fields = ('filter', 'values',)
    readonly_fields = ('filter',)

    def has_delete_permission(self, request, obj=None):
        return False

    def formfield_for_manytomany(self, db_field, request, **kwargs):
        # тут нужно ограничить выбор значений атрибута товара
        # для этого сюда НУЖНО ПРОКИНУТЬ соседнее поле filter,
        # чтобы достать его набор значений (variant_set)
        field = super(VariantAttributeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
        if db_field.name == 'values':
            print(field)
        return field


# Редактирование товара
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    model = Product
    inlines = [VariantAttributeInline]

Thank you for your attention to the question.
I will be glad to any proposed solutions.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
A
Anatoly Scherbakov, 2017-05-31
@ri_gilfanov

The field filteris not editable. I think what you need to access is not the field, but the model object VariantAttribute. Right?
If so, then this option should suit you: https://stackoverflow.com/a/22003809/1245471 - here they suggest creating a separate class for the form and filtering in it, and then set this class for VariantAttributeInline(attribute form, if I don't changes memory).

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question