Diego Avila
Diego Avila

Reputation: 738

Show all data from ManyToMany Fields

I'm try to get all data from relationship manytomany, this is my model

class Product(models.Model):

    name = models.CharField(max_length=100)
    image = models.FileField(upload_to='products/', null=True)
    price = models.FloatField(default=0)
    stock = models.CharField(max_length=15)
    categories = models.ManyToManyField(Categories)
    def __str__(self):
        return self.name

class Categories(models.Model):
   name =  models.CharField(max_length=100)
    def __str__(self):
        return self.name

for get the data from products try this:

class Products(ListView):

    model = Product
    template_name = "products.html"

on the template return similar to this:

_______________________________
# | name  | price | Categories |
________________________________
1 | Pepsi | 1.25  |            |
2 | Choco.| 2.50  |            |

for generate this table i use a cycle for:

{% for product in object_list %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ product.name }}</td>
<td>{{ product.price }}</td>
<td>{{ product.categories }}</td>
{% endfor %}

in this case return empty the categories selected on the product..please any suggest.. thanks !!

Upvotes: 1

Views: 90

Answers (1)

Serafeim
Serafeim

Reputation: 15104

Notice that the categories attribute of Product is a many to many relation thus it resolves to another query with multiple objects. It is another queryset just line your object_list, thus you need to explicitly tell the template how to print that.

Here's a sample solution:

{% for product in object_list %}
    <tr>
    <td>{{ forloop.counter }}</td>
    <td>{{ product.name }}</td>
    <td>{{ product.price }}</td>
    <td>
        {% for category in product.categories.all %}
            {{ category }}
        {% endfor %}
    </td>
{% endfor %}

Or you could use the django join template tag (https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#join):

{% for product in object_list %}
    <tr>
    <td>{{ forloop.counter }}</td>
    <td>{{ product.name }}</td>
    <td>{{ product.price }}</td>
    <td>{{ product.categories.all|join:", " }}</td>
{% endfor %}

Some more quick comments to help you since you are starting Django:

  • class Categories is not a very good name for your model. I propose naming it class Category since each Category instance will correlate to a single category.
  • class Products is a bad name for a view. I propose naming it class ProductListView so you'll know that it is a ListView of your products. Also keep a similar naming scheme to all your views.
  • Using a FloatField to save prices (monetary values) is always problematic. You'll have strange values when you try to do aggregates on that column (i.e adding or taking averages). This is because of how float numbers work (i.e some values cannot be represented exactly by a float but you'll get a very close approximation instead like instead of 1.3 you'll have 1.2999999999999 with the float). Use a DecimalField instead to properly save monetary values.
  • You can pass a context_object_name attribute to your ListView so that you could use it instead of object_list. Thus, if you'd defined your ProductListView like this:
    class ProductListView(ListView):
        model = Product
        template_name = "products.html"        
        context_object_name = 'products'

you'd be able to do {% for product in products %} ... in your template.

We could explicitly tell the view which template to use by adding a template_name attribute to the view, but in the absence of an explicit template Django will infer one from the object’s name. In this case, the inferred template will be "books/publisher_list.html" – the “books” part comes from the name of the app that defines the model, while the “publisher” bit is just the lowercased version of the model’s name.

Use template_name only if you intent to add a 2nd list view for a class to make it explicit that this is intentional.

Upvotes: 2

Related Questions