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.
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
# 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)
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.