access to data ManyToMany django template - django

I try to display a menu category ---> Subcategory ---> products.
I use a context_processor to display all the categories and submenus .
I need to get products based on category and subcategory
I can only display django.db.models.fields.related.ManyRelatedManager at object ....
class Categorias(models.Model):
nome_categoria = models.CharField(max_length=100)
class Subcategoria(models.Model):
nome_subcategoria = models.CharField(max_length=100)
class Product(models.Model):
categoria = models.ManyToManyField('Categorias')
subcategoria = models.ManyToManyField('Subcategoria')
context.py
def menu(request):
return {'menucategoria': Categorias.objects.all(),}
def submenu(request):
return {'submenu': Subcategoria.objects.all(),}
menu.html
{% for c in menucategoria %}
<ul>
<li class="block">
<input type="checkbox" name="item" id="{{c.id}}" />
<label for="{{c.id}}">{{c}}</label>
<ul class="options">
{% for p in produtos.subcategoria.all %}
<li>{{p}}</li>
{% endfor %}
</ul>
</li>
</ul>
{% endfor %}

{% for p in produtos.subcategoria.all %}
In Python you would get a TypeError: 'Manager' object is not iterable exception, but in templates if fails silently...
There are some more tweaks to be done... You seem to got it wrong with the related_name. Related name is used for reversing relationships, not following them. So probably this is what you're after:
class Categoria(models.Model): # singular!
nome_categoria = models.CharField(max_length=100)
class Subcategoria(models.Model):
nome_subcategoria = models.CharField(max_length=100)
class Product(models.Model):
# using ForeignKey instead of ManyToMany. Guessed so because "categoria" is singular, right?
categoria = models.ForeignKey('Categoria', related_name='produtos') # plural in related_name, and "products" not "category"
subcategoria = models.ForeignKey('Subcategoria', related_name='produtos') # plural in related_name, and "products" not "category"
Now you can do stuff like:
{% for p in categoria.produtos.all %}
somestuff...
{% for sc in p.subcategoria.all %}
somemorestuff...
P.S.
You can leave out the related_name altogether. A default related name will be used: product_set in this example.

Related

Django PostgreSQL – Efficiently fetch recursive category structure

I have a model which looks like this:
class Category(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField()
parent = models.ForeignKey(
'categories.Category',
null=True,
blank=True,
on_delete=models.CASCADE,
related_name='categories'
)
basically, in the parent field, it references itself. If a parent is set to None, it's the root category.
I use it to build a hierarchy of categories.
What would be the most efficient way to:
fetch all the objects through the hierarchy
display them in a template?
For some reason, select_related does not seem to lead to performance improvements here.
I also found this: How to recursively query in django efficiently?
But had a really hard time applying it to my example, because I still don't really understand what's going on. This was my result:
WITH RECURSIVE hierarchy(slug, parent_id) AS (
SELECT slug, parent_id
FROM categories_category
WHERE parent_id = '18000'
UNION ALL
SELECT sm.slug, sm.parent_id
FROM categories_category AS sm, hierarchy AS h
WHERE sm.parent_id = h.slug
)
SELECT * FROM hierarchy
Would appreciate any help.
Thanks!
One possible solution can be using https://django-mptt.readthedocs.io/en/latest/overview.html#what-is-django-mptt
MPTT is a technique for storing hierarchical data in a database. The
aim is to make retrieval operations very efficient.
The trade-off for this efficiency is that performing inserts and moving items around the tree is more involved, as there’s some extra work required to keep the tree structure in a good state at all times.
from django.db import models
from mptt.models import MPTTModel, TreeForeignKey
class Category(MPTTModel):
name = models.CharField(max_length=50)
slug = models.SlugField()
parent = TreeForeignKey(
'self',
null=True,
blank=True,
on_delete=models.CASCADE,
related_name='children'
)
class MPTTMeta:
order_insertion_by = ['name']
You can use the django-mptt template tag as this:
{% load mptt_tags %}
<ul>
{% recursetree categories %}
<li>
{{ node.name }}
{% if not node.is_leaf_node %}
<ul class="children">
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
There is a tutorial and more information int library docs.
I had the same problem and ended up creating the following function that hits the database once, then sorts out the heirarchy and returns a dict:
def get_category_tree():
categories = Category.objects.order_by('name')
itemtree = {}
# Add 'children' attribute to each category; populate dict
for category in categories:
category.children = {}
itemtree[category.pk] = category
# Add categories to 'children'
for key,value in itemtree.items():
if value.parent_id:
itemtree[value.parent_id].children[key] = value
# Return top-level items
return {k:v for k,v in itemtree.items() if not v.parent_id}
Each value of the returned dict is a top-level Category object which has a children attribute.
You can render it in the template by looping through the dict values. The following example will handle three levels of heirarchy:
<ul>
{% for level1 in category_tree.values %}
<li>
{{ level1.name }}
{% if level1.children %}
<ul>
{for level2 in level1.children.values %}
<li>{{ level2.name }}
{% if level2.children %}
<ul>
{for level3 in level2.children.values %}
<li>{{ level3.name }}</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
If you need to render many levels of the heirarchy, you could consider using template recursion. Have a read of the following question and answers to determine if that might be suitable: Represent a tree of objects in Django template

Models inherit fields from other models in Django

I have the following models. I am trying to get the newlistitem model to inherit the same image from the above, if that makes sense. I see that I passed through user as a parameter when calling listitem.user and it works fine, but can't seem to grab the picture of the related object.
HTML Render
I am returning both objects to the form and call
{% for item in listitems %}
<div id = "indivlistitem">
<b>{{item.list_name|title}}</b>
<li><img src="/media/{{ item.list_picture }}"/></li>
<li>{{item|title}}</li>
</div>
{% endfor %}
#MODELS
from django.db import models
from django.contrib.auth.models import User
class newlist(models.Model):
user = models.ForeignKey(User)
list_name = models.CharField(max_length = 100)
picture = models.ImageField(upload_to='profiles/')
def __str__(self):
return self.list_name
class newlistitem(models.Model):
user = models.ForeignKey(User)
list_name = models.ForeignKey(newlist)
list_item = models.CharField(max_length = 200)
list_picture = models.ImageField(newlist.picture)
def __str__(self):
return self.list_item
First things first, list_picture = models.ImageField(newlist.picture)
is not going to work. However, it did provide some insight into what you're trying to do.
Since you already have a foreign key to a list in the newlistitem model (your list_name field), you can access the picture that it's linked to by traversing the foreign key, as such.
You'll note that I've also used the url property that all ImageFields contain, to automatically populate the URL of the picture:
{% for item in listitems %}
<div id = "indivlistitem">
<b>{{item.list_name|title}}</b>
<li><img src="{{ item.list_name.picture.url }}"/></li>
<li>{{item|title}}</li>
</div>
{% endfor %}
UPDATE
Some of the pictures that you are trying to access are blank, so you will need to validate that there is an image associated with each entry.
{% for item in listitems %}
<div id = "indivlistitem">
<b>{{item.list_name|title}}</b>
{% if item.list_name.picture %}
<li><img src="{{ item.list_name.picture.url }}"/></li>
{% endif %}
<li>{{item|title}}</li>
</div>
{% endfor %}

How can I get "ManyToManyField" values in the search filters of Haystack?

I'm implementing a filtering functionality using haystack with elasticsearch. Now, I'm getting most of all the fields to filter however I can't get the ManyToManyField values. In the browser is appearing like the physical address not the readable value:
I've got in my model the code below:
class StudyLevel(models.Model):
name_level = models.CharField(max_length=100, unique=True)
class Institution(models.Model):
.
.
level_study = models.ManyToManyField(StudyLevel, null=True)
.
.
In the search_indexes is the following:
class InstitutionIndex(indexes.SearchIndex, indexes.Indexable):
.
.
level_study = indexes.CharField(model_attr='level_study', faceted=True)
Now, in the search html template is the problem:
{% if facets.fields.level_study %}
<div>
<h4>Study Level</h4>
<ul>
{% for level_study in facets.fields.level_study.all %}
<li>{{ level_study.0 }} ({{ level_study.1 }})</li>
{% endfor %}
</ul>
</div>
{% endif %}
How can I solve it to get those values?
Add a Facet field for filtering
level_study = FacetMultiValueField()
and then fill it with the items from your Many-To-Many relation:
def prepare_level_study(self, obj):
return [l.name for l in obj.level_study.all()]
and don't forget to add
.facet("level_study")
to your SearchQuerySet().

Django regroup many to many field

I'm having some trouble making this work on the template, wonder if I could get some help:
I have a table with plants in it, and the plant table has a many-to-many relationship with the category table. Such that a plant can be in one or more categories.
I'm using Django 1.5, here are the models:
class Plant(models.Model):
scientific_name = models.CharField(max_length=128, unique=True)
category = models.ManyToManyField(Category)
...
class Category(models.Model):
category = models.CharField(max_length=128)
...
And the view:
class PlantListView(ListView):
context_object_name='plant_list'
template_name='plants/index.html'
model = Plant
def get_queryset(self):
return self.model.objects.all().order_by('category')
I've tried this:
{% regroup plant_list by category as category_list %}
<ul>
{% for category in category_list %}
<li>{{ category.grouper }}
<ul>
{% for plant in plant_list.list %}
<li>{{ plant.scientific_name }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
But all I get is:
<django.db.models.fields.related.ManyRelatedManager object at 0x1053bfb90>
<django.db.models.fields.related.ManyRelatedManager object at 0x1053bfbd0>
<django.db.models.fields.related.ManyRelatedManager object at 0x1053bfd50>
Of course, what I really want is:
Annuals
Ageratum houstonianum
Abutilon hybridum
Acalypha hispida
Bulbs Perennial
Ageratum houstonianum
Allium giganteum
Allium karataviense
... etc ...
Note that the same plant can exist in multiple categories, and should be listed in each.
What am I doing wrong?
Thanks in advance!!
Well, turned out it was easier (and probably better) to make a Dict of all the results, with category as the index.

Display name of many to many relation in template

I try to display the names of fields from a Model in my template.
This works fine with any type of fields except ManyToManyField.
I use this function in my models.py to return all fields.
def get_all_fields(self):
"""Returns a list of all field names on the instance."""
fields = []
# only display fields with values and skip some fields entirely
if f.editable and value and f.name not in ('id','lastname','firstname') :
fields.append(
{
'label':f.verbose_name,
'name':f.name,
'value':value,
})
return fields
In my template I use this loop to display all fields:
{% for f in modelname.get_all_fields %}
<td>{{f.label|capfirst}}</td><td>{{f.value|escape|urlize|linebreaks}}</td>
{% endfor %}
As mentioned before, this works fine with all fields except ManyToManyFields.
For example one of my M2M relations looks like this:
family = models.ManyToManyField('family', related_name="family", null=True, blank=True)
I'd be thankful for every hint that helps solving this.
Regards
Conrad
Try to specify verbose_name argument for ManytoManyfield
family = models.ManyToManyField('family',verbose_name=u'trampampam', related_name="family", null=True, blank=True)
You write {{f.value|escape|urlize|linebreaks}}, which displays the value of the field. However, the value of a M2M relation is a set of object instances and you need to iterate again over the set (if that is the result you want):
{% load m2m_filter %}
{% for f in modelname.get_all_fields %}
<td>{{f.label|capfirst}}</td>
<td>
{% if f.value|is_m2m %}
{% for object in f.value.objects.all %}
{{ object|escape|urlize|linebreaks }}
{% endfor %}
{% else %}
{{f.value|escape|urlize|linebreaks}}
{% endif %}
</td>
{% endfor %}
and you also have to create the filter
m2m_filter.py
from django import template
from django.db import models
register = template.Library()
def is_m2m(value):
return type(value) == models.ManyToManyField *
register.filter('is_m2m', is_m2m)
* I guess, it's a different type; just check that