Displaying Nested ManyToMany Relation - django

I'm having nested many to many relation in my models.py and I've got the display partially working. I have 2 questions:
Is there a way to simplify the presentation, e.g. by inlineformset?
How to I access nested context variables in the template form (see line {% for objective in selected_objectives %} )?
Please let me know if there is a way to make my question more clear
models.py
class Process(models.Model):
title = models.CharField(max_length=200)
desc = models.TextField('process description', blank=True)
def __str__(self):
return self.title
class Objective(models.Model):
process = models.ManyToManyField(Process, verbose_name="related processes", blank=False)
title = models.CharField(max_length=200)
desc = models.TextField('objective description', blank=True)
def __str__(self):
return self.title
class Risk(models.Model):
objective = models.ManyToManyField(Objective, verbose_name="related objectives", blank=False)
title = models.CharField(max_length=200)
desc = models.TextField('risk description', blank=True)
def __str__(self):
return self.title
views.py
#login_required
def detailed_list(request):
#context = RequestContext(request)
obj = []
ri = []
all_processes = Process.objects.order_by('id') #[:1]
for p_index,p in enumerate(all_processes):
obj.append(p.objective_set.all()) #appending objectives for each process
for o_index,o in enumerate(obj[p_index]):
ri.append(o.risk_set.all().values()) #appending risks for each objective
context = {'all_processes': all_processes,
'selected_objectives': obj,
'selected_risks': ri
}
return render(request, 'repository/detailed.html', context)
template detailed.html
<p>Create new Process
</p>
{% if all_processes %}
No: {{ all_processes|length }}
<ul>
{% for process in all_processes %}
<li>{{ process.title }} {{ forloop.counter0 }}</li>
<ul>
{% if selected_objectives %}
{% for objective in selected_objectives %}
<!-- see here --> <li>{{ objective.title }} {{ forloop.counter0 }} - {{ objective.desc }}</li>
{% endfor %}
{% else %}
<p>No objectives are available.</p>
{% endif %}
</ul>
{% endfor %}
</ul>
{% else %}
<p>No processes are available.</p>
{% endif %}

all you have to do is just pass the Process object as the context to your template
context = {'all_processes': all_processes}
and in you template :
<p>Create new Process
</p>
{% if all_processes %}
No: {{ all_processes|length }}
<ul>
{% for process in all_processes %}
<li>{{ process.title }} {{ forloop.counter0 }}</li>
<ul>
{% if all_processes.objective_set.all %}
{% for objective in all_processes.objective_set.all %}
<li>{{ objective.title }} {{ forloop.counter0 }} - {{ objective.desc }}
</li>
{% endfor %}
{% else %}
<p>No objectives are available.</p>
{% endif %}
</ul>
{% endfor %}
</ul>
{% else %}
<p>No processes are available.</p>
{% endif %}
<!-- this is if you want to show risks -->
{% for process in all_processes %}
{% for objective in all_processes.objective_set.all %}
{% for risk in objective.risk_set.all %}
{{ risk.desc }}
{% endfor %}
{% endfor %}
{% endfor %}
I hope this is what you were expecting !

Related

access foreign key via a manytomany relationship in django

I have a foreign key that is in turn a many to many relation like the following:
Persons<-M2M->Role(ManyToOne)->Name
The model:
class Person(models.Model):
mail=models.EmailField()
firstName=models.CharField(max_length=200)
lastName=models.CharField(max_length=200)
phoneNumber=PhoneNumberField()
streetAdress=models.CharField(max_length=200)
zipcode=models.CharField(max_length=200)
city=models.CharField(max_length=200,default="Göteborg")
country=models.CharField(max_length=200,default="Sweden")
def __str__(self):
return "%s %s" % (self.firstName,self.lastName)
class Meta:
ordering = ('firstName','lastName')
class Role(models.Model):
role=models.CharField(max_length=200)
person=models.ManyToManyField(Person)
def __str__(self):
return self.role
class Meta:
ordering = ('role',)
class Name(models.Model):
name=models.CharField(max_length=200)
role=models.ForeignKey(Role,on_delete=models.CASCADE)
def __str__(self):
return self.name
class Meta:
ordering = ('name',)
class Address(models.Model):
I can acces the manyTomany field from persons and roles but I cannot access names.
the template:
{% extends "artdb/base.html" %}
{% block content1 %}
<ul>
{% for p in ans %}
<h5>First name: {{p.firstName}}</h5>
<h5>Last name: {{p.lastName}}</h5>
<h5>Phone: {{p.phoneNumber}}</h5>
<h5>Adress: {{p.streetAdress}}</h5>
<h5>Zip Code: {{p.zipcode}}</h5>
<h5>City: {{p.city}}</h5>
<hr>
{% endfor %}
</ul>
{% endblock content1 %}
{% block content2 %}
<ul>
{% for p in ans %}
{% for r in p.role_set.all %}
<h5>{{ r.role }}</h5>
{% endfor %}
{% endfor %}
<hr>
</ul>
{% endblock content2 %}
{% block content3 %}
<ul>
{% for p in ans %}
{% for r in p.role_set.all %}
<h5>{{ r.name }}</h5>
{% endfor %}
{% endfor %}
<hr>
</ul>
{% endblock content3 %
I konow that I have to iterate to get manyTomany, but is it the same with oneTomany?
how do I access Names from Persons via Role?
}
here is the solution for those interested. (thanks daniel roseman). Iterate through every relation. Here the first relation is many to many and the second many to one:
{% block content3 %}
<ul>
{% for p in ans %}
{% for r in p.role_set.all %}
{% for n in r.name_set.all %}
<h5>{{ n }}</h5>
{% endfor %}
{% endfor %}
{% endfor %}
<hr>
</ul>
{% endblock content3 %}

is_paginated not working properly for class based views in Django

book_list.html
{% extends "base.html" %}
{% block content %}
<h3> Available books </h3>
{% if book_list %}
<ul>
{% for book in book_list %}
<li> {{book.title }} <small> by {{book.author }}</small></li>
<p>{{ book.summary }}
{% endfor %}
<ul>
{% endif %}
{% if is_paginated %}
<div class="pagination">
<span class="page-links">
{% if page_obj.has_previous %}
previous
{% endif %}
<span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
next
{% endif %}
</span>
</div>
{% else %}
<h4> pagination not working</h4>
{% endif %}
{% endblock %}
class in views.py :
class BookListView(generic.ListView):
model = Book
paginate_by = 2
queryset =Book.objects.all()
urls.py :
urlpatterns =[
url('^$',views.index, name ='index'), # matching with an empty string
url('^books/$',views.BookListView.as_view(),name ='books'), #the one to which I am adding the paginator
url('^book/(?P<pk>\d+)/$',views.BookDetailView.as_view(),name='book-detail'),
]
Book model :
class Book(models.Model):
title=models.CharField(max_length=100)
author = models.ForeignKey('Author',on_delete=models.SET_NULL,null = True)
summary = models.TextField(max_length=200,help_text="Enter the description")
isbn = models.CharField('ISBN',max_length=13 ,help_text='13 Character ISBN number' )
genre = models.ManyToManyField(Genre,help_text = 'selct a genre for this book')
class Meta:
ordering =["title"]
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('book-detail',args=[str(self.id)] )
The book_list gets rendered perfectly, the problem is with the paginator, the is_paginated condition is not working and it executes the else statement, I have been trying for more than 3 hours, but couldn't find a solution, What Am I missing here ?
Django version : 1.11.2
Python : 3.5
Edit :
update 1: The problem was the paginate_by value was two, and the total items to display was also two hence it didn't initiate the is_paginated tag,It worked fine when I added one item more than paginate_by value.
use this, you had some problem with the if condition
{% extends "base.html" %}
{% block content %}
<h3> Available books </h3>
{% if object_list %}
<ul>
{% for book in object_list %}
<li> {{book.title }} <small> by {{book.author }}</small></li>
<p>{{ book.summary }}
{% endfor %}
<ul>
{% if is_paginated %}
<div class="pagination">
<span class="page-links">
{% if page_obj.has_previous %}
previous
{% endif %}
<span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
next
{% endif %}
</span>
</div>
{% else %}
<h4> pagination not working</h4>
{% endif %}
{% else %}
<h4> No book</h4>
{% endif %}
{% endblock %}

Django: get profiles having auth.Group as foreign key

I have an model which uses auth.models.Group as foreign key called Dashboard:
class Dashboard(models.Model):
d_name = models.CharField(max_length=200)
d_description = models.CharField(max_length=200)
d_url = models.CharField(max_length=200)
d_status = models.CharField(max_length=200)
owner = models.ForeignKey(Group)
def __str__(self):return self.d_name
my views.py is:
def custom_login(request):
if request.user.is_authenticated():
return HttpResponseRedirect('dashboards')
return login(request, 'login.html', authentication_form=LoginForm)
def custom_logout(request):
return logout(request, next_page='/')
def user(request):
context = {'user': user, 'groups': request.user.groups.all()}
return render_to_response('registration/dashboards.html', context,
context_instance=RequestContext(request))
and here using this dashboards.html I want to display the dashboards by using the Group_name which i will get as a result of group.name:
{% extends "base.html" %}
{% block content %}
{% if user.is_authenticated %}
<p>Welcome, {{ request.user.get_username }}. <br/>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
<ul>
{% for group in groups %}
<li>
<strong>{{ group.name }}<strong> -
{{ dashboards.d_name }}{% if not forloop.last %},{% endif %}
</li>
{% endfor %}
</ul>
{% endblock %}
here I have mentioned all the supporting information for my problem, please let me know if there are any solution.
To access the list of Dashboards for the Group use the group.dashboard_set queryset:
{% for group in groups %}
<li>
<strong>{{ group.name }}</strong> -
{% for dashboard in group.dashboard_set.all %}
{{ dashboard.d_name }}{% if not forloop.last %},{% endif %}
{% endfor %}
</li>
{% endfor %}
This queryset is called "backward relationship".
views.py
def user(request):
user= request.user
groups = request.user.groups.all()
dashboards = Dashboard.objects.filter(owner=groups)
context = {
'user': user,
'groups': groups,
'dashboards': dashboards,
}
return render_to_response('registration/dashboards.html', context, context_instance=RequestContext(request))
and dashboards.html
{% extends "base.html" %}
{% block content %}
{% if user.is_authenticated %}
<p>Welcome, {{ request.user.get_username }}. <br/>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
<ul>
{% for group in groups %}
<li>
<strong>you belongs to::{{ group.name }}</strong> </li>
{% endfor %}
</ul>
<strong>#Dashboards available are::</strong>
{% for Dashboard in dashboards %}
<ol>
<li>{{ Dashboard.d_name }}-{{ Dashboard.owner }}-{{Dashboard.d_description}}</li> </ol>
{% endfor %}
{% endblock %}
this works good and neat,,,,

Using MPTT do get_children in templates (at runtime)

my template receives a variable called categories, and I want to list the categories that are "sons" of its father categories
this is my code in template
{% for category,structure in categories|tree_info %}
{% if structure.new_level %}
<li>{{ category.name }} </li>
{% endif %}
{% for level in structure.closed_levels %}
<li>{{level.name}}
{% endfor %}
<ul class="noJS">
{% for cat in category.get_children|tree_info %}
<li>{{ cat.name }}aa </li>
{% endfor %}
</ul>
{% endfor %}
this is the model
class Category(MPTTModel):
name = models.CharField(max_length=50, unique=True)
description = models.TextField(blank=True)
parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
class MPTTMeta:
order_insertion_by = ['name']
def __unicode__(self):
return self.name
any idea?
This worked
{% for category in categories %}
<li>
{{ category.name }}
<ul class="noJS">
{% for cat in category.get_children %}
<li>{{ cat.name }} </li>
{% endfor %}
</ul>
</li>
{% endfor %}

Django Reverse Query in Template

I have models like this
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __unicode__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length=255)
I want to list all blogs in a page. I have written a view such that
def listAllBlogs(request):
blogs= Blog.objects.all()
return object_list(
request,
blogs,
template_object_name = "blog",
allow_empty = True,
)
And I can display tagline of blog such that in view
{% extends "base.html" %}
{% block title %}{% endblock %}
{% block extrahead %}
{% endblock %}
{% block content %}
{% for blog in blog_list %}
{{ blog.tagline }}
{% endfor %}
{% endblock %}
But I would like to show, such thing blog__entry__name but I don't know how can I achive this in template.
Also, there may be no entry in a blog. How can I detect in template ?
Thanks
To access blog entries (Related Manager): blog.entry_set.all
To do other actions if blog have no entries, you have the {% empty %} tag that is executed when the set is empty.
{% block content %}
{% for blog in blog_list %}
{{ blog.tagline }}
{% for entry in blog.entry_set.all %}
{{entry.name}}
{% empty %}
<!-- no entries -->
{% endfor %}
{% endfor %}
{% endblock %}
based on your code you could do the following.
{% block content %}
{% for blog in blog_list %}
{{ blog.tagline }}
{% for entry in blog.entry_set.all %}
{{entry.name}}
{% endfor %}
{% endfor %}
{% endblock %}