# M2M relationship, labels / names of the form fields displayed in HTML

**URL:** <https://forum.djangoproject.com/t/m2m-relationship-labels-names-of-the-form-fields-displayed-in-html/30802>\
**Category:** Forms & APIs\
**Created:** [May 4, 2024, 3:14am UTC](https://forum.djangoproject.com/t/m2m-relationship-labels-names-of-the-form-fields-displayed-in-html/30802 "2024-05-04T03:14:38Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![IlerioDef](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/ileriodef/32/21038_2.png) [@IlerioDef](https://forum.djangoproject.com/u/IlerioDef)\
**Post date:** [May 4, 2024, 3:14am UTC](https://forum.djangoproject.com/t/m2m-relationship-labels-names-of-the-form-fields-displayed-in-html/30802/1 "2024-05-04T03:14:38Z")

</div>

Hi!  
First, allow me to describe my models etc:

```python

**models.py**

class Item(models.Model):
    name = models.CharField(max_length=50)
    ...

class Order(models.Model):
    table = models.ForeignKey(
        "Table", on_delete=models.SET_NULL, null=True, blank=True
    )
    items = models.ManyToManyField(
        Item, through="OrderItem", related_name="orders_items"
    ...

    def get_items(self):
        return self.orderitem_set.prefetch_related("item")

class OrderItem(models.Model):
    order = models.ForeignKey(Order, on_delete=models.CASCADE)
    item = models.ForeignKey(
        Item, on_delete=models.SET_NULL, null=True, blank=True
    )
    quantity = models.PositiveIntegerField(default=1)
    date_added = models.DateTimeField(auto_now_add=True)
    rating = models.PositiveSmallIntegerField(default=0, null=True, blank=True)

**views.py**

class ReviewOrderView(LoginRequiredMixin, View):
    template_name = "menu/submit_review_form.html"

    OrderItemFormSet = inlineformset_factory(
        Order,
        OrderItem,
        fields=["rating"],
        extra=0,
        can_delete=False,
        labels="item_names",
)

    def get(self, request, *args, **kwargs):
       # Please ignore the Table model reference here, it has nothing to do with the problem        
        order = (
            Table.objects.get(id=request.user.table.id).get_order()  
        )  
        item_names = order.get_items().values("item__name")

        form = self.OrderItemFormSet(
            instance=order,
        )
        return render(request, self.template_name, {"form": form})

    def post(request, *args, **kwargs):
        pass

```

As you can see, I have two models in an m2m relationship with a through-model (`OrderItem`) that contains additional data, the rating.

I was trying to make a form that will allow users to submit their reviews on items in the order. Since it’s actually a number of small forms with the field `rating` that depends on the number of items in the order, I thought the correct way to do so is to implement a formset\_factory—or, more specifically, an inlineformset\_factory.  
I was able to get this:

 ![image](https://us1.discourse-cdn.com/flex026/uploads/djangoproject/original/3X/9/f/9fef322a7fd29fa731887c88abf3db80b5c6c0f3.png)  
This correlates with the number of items in order, but I want the actual name of the `Item ("item__name")` instead of the word `Rating:`

How can I do that?  
Will appreciate any help.

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [May 4, 2024, 7:25pm UTC](https://forum.djangoproject.com/t/m2m-relationship-labels-names-of-the-form-fields-displayed-in-html/30802/2 "2024-05-04T19:25:09Z")

</div>

Yes it can be done. I can see a couple different ways to possibly do it, I’m not sure what’s going to be easiest.

You could [override the `add_fields` method on the formset class](https://docs.djangoproject.com/en/5.0/topics/forms/formsets/#adding-additional-fields-to-a-formset), to set the `form.fields['a_field'].label` attribute, or you could create a custom model form that sets the label in _its_ ` __init__ ` method (but you _might_ need to [override `get_form_kwargs`](https://docs.djangoproject.com/en/5.0/topics/forms/formsets/#passing-custom-parameters-to-formset-forms) to pass the data through that you need).

The model form being created should have the `instance` available for accessing the specific object for which the form is being created. Otherwise, the formset itself has the queryset of related objects available in `self.queryset` if you need to get the specific object.

I haven’t had a chance to try either one of these, so this is all just a semi-educated guess, but one of them _should_ work.

---

<div class="post-metadata">

**Author:** ![IlerioDef](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/ileriodef/32/21038_2.png) [@IlerioDef](https://forum.djangoproject.com/u/IlerioDef)\
**Post date:** [May 10, 2024, 3:41am UTC](https://forum.djangoproject.com/t/m2m-relationship-labels-names-of-the-form-fields-displayed-in-html/30802/3 "2024-05-10T03:41:01Z")

</div>

Hi Ken!  
Thank you for your swift reply and excuse me for my delayed answer.  
So far, I was able to make a bit ugly solution, not without outside help, that looks like this:

```python

**forms.py**

def item_field(db_field, **kwargs):
    if db_field.name == "item":
        return db_field.formfield(**kwargs, disabled=True)

    return db_field.formfield(**kwargs)

ReviewFormset = modelformset_factory(
    OrderItem,
    formfield_callback=item_field,
    fields=["rating", "item"],
    extra=0,
)

**views.py**

class ReviewOrderView(LoginRequiredMixin, View):
    template_name = "menu/submit_review_form.html"

    def get_queryset(self): #yes, I know that there is no such method by default in the generic View 
        return (
            Table.objects.get(id=self.request.user.table.id)
            .get_order()
            .get_items()
        )

    def get(self, request, *args, **kwargs):
        queryset = self.get_queryset()
        review_formset = ReviewFormset(queryset=queryset)
        context = {"forms": review_formset}
        return render(request, self.template_name, context)

    def post(self, request, *args, **kwargs):
        review_formset = ReviewFormset(data=request.POST)
        if review_formset.is_valid():
            review_formset.save()
        return_url = reverse_lazy("menu:all")
        return HttpResponseRedirect(return_url)

```

That results in:  
 ![image](https://us1.discourse-cdn.com/flex026/uploads/djangoproject/original/3X/4/9/497c9861d54ba3b8aa5440b421f981f35db88de9.png)

And it works 🤔
