简体   繁体   中英

Setting value of Django M2M through relationship via ModelForm

I am working on a set of product / category relationships in a Django application.

A product can belong to any category and needs to be ordered within that category, I am trying to do this using a Many to Many relationship with a "through=" option.

When a POST request is made via Ajax it takes the form of b'ordered_products=4&ordered_products=5' , I received an error straight after the forms __init__ call that "5 is not one of the available choices" where 5 is the valid id of an OrderedCategoryManagedProduct object.

models.py

class Category(models.Model):
    name = models.CharField(max_length=128)
    slug = models.SlugField(max_length=128, unique=True)


class Product(models.Model):
    name = models.CharField(max_length=128)
    category_management = models.ManyToManyField(
        Category,
        related_name="category_managed_products",
        through="OrderedCategoryManagedProduct",
        blank=True,
        verbose_name="Category Management",
    )


class OrderedCategoryManagedProduct(SortableModel):
    category = models.ForeignKey(
        Category, on_delete=models.CASCADE, related_name="cm_products"
    )
    product = models.ForeignKey(
        Product, on_delete=models.CASCADE, related_name="cm_categories"
    )

    class Meta:
        ordering = ["sort_order"]

    def get_ordering_queryset(self):
        return self.product.category_management()


class SortableModel(models.Model):
    sort_order = models.IntegerField(db_index=True, null=True)

    class Meta:
        abstract = True

views.py

# POST = <QueryDict: {'ordered_products': [5, 4]}>
@staff_member_required
@permission_required("menu.manage_menus")
def ajax_reorder_menu_items(request, category_pk):
    category = get_object_or_404(Category, pk=category_pk)
    form = ReorderCategoryProductsForm(request.POST, instance=category)
    status = 200
    ctx = {}
    if form.is_valid():
        form.save()
    elif form.errors:
        status = 400
        ctx = {"error": form.errors}
    return JsonResponse(ctx, status=status)

forms.py

class ReorderCategoryProductsForm(forms.ModelForm):
    ordered_products = OrderedModelMultipleChoiceField(
        queryset=OrderedCategoryManagedProduct.objects.none()
    )

    class Meta:
        model = Category
        fields = ["id"]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.instance:
            self.fields["ordered_products"].queryset = self.instance.cm_products.all()
        pass

    def save(self):
        for sort_order, category in enumerate(self.cleaned_data["ordered_products"]):
            category.cm_products.sort_order = sort_order
            category.save()
        return self.instance


class OrderedModelMultipleChoiceField(forms.ModelMultipleChoiceField):
    def clean(self, value):
        qs = super().clean(value)
        keys = list(map(int, value))
        return sorted(qs, key=lambda v: keys.index(v.pk))

Big thanks to the #django IRC channel for the help on this, if anyone suffers the same problem the correct code was as follows:

class ReorderCategoryProductsForm(forms.ModelForm):
    ordered_products = OrderedModelMultipleChoiceField(
        queryset=OrderedCategoryManagedProduct.objects.all()
    )

    class Meta:
        model = Category
        fields = ["id"]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.instance:
            self.fields["ordered_products"].queryset = self.instance.cm_products.all()
        pass

    def save(self):
        for sort_order, ocmp in enumerate(self.cleaned_data["ordered_products"]):
            ocmp.sort_order = sort_order
            ocmp.save()
        return self.instance

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM