Django multi-level extends not appearing - django

With Django 1.10 I am building a blog/portfolio hybrid website. Besides Mysite, I have two apps, Blog and Portfolio. Within blog/templates/ I have an index.html that displays content from both Blog and Portfolio (the 15 most recent blog posts and 5 most recent portfolio projects).
So the index.html page looks like this:
http://imgur.com/y2UqBSS
As you can see data is not appearing. However, it does when I navigate to the Blog page and Portfolio page. For example, the Blog page:
http://imgur.com/7P922Ga
I am figuring that this issue is related to the mult-level extends that I have going on. Because the Blog and Portfolio pages both display content from the database, it makes me think that the models are O.K but something is up with the views. The index.html extends the base_generic.html template, and my recent_blog_posts.html and recent_portfolio_pieces.html extends the index.html.
I am not sure how to remedy this issue. Any suggestions what I'm doing wrong?
Project structure
mysite/
---blog/
------static/
---------css/
---------images/
------------blogpostimages/
------------favicon.ico
------templates/
---------blog/
------------blog_post.html
------------blog_list.html
------------recent_blog_posts.html
---------base_generic.html
---------index.html
---------bio.html
---------resume.html
------admin.py
------apps.py
------models.py
------tests.py
------urls.py
------views.py
---portfolio/
------static/
---------css/
---------images/
------------portfoliopieceimages/
------templates/
---------portfolio/
------------portfolio_piece.html
------------portfolio_list.html
------------recent_portfolio_pieces.html
------admin.py
------apps.py
------models.py
------tests.py
------urls.py
------views.py
---mysite/
------settings.py
------urls.py
------wsgi.py
manage.py
db.sqlite3
requirements.txt
blog/views.py
from django.shortcuts import render
from django.views import generic
# Create your views here.
from .models import Blog
def index(request):
"""
View function for home page of site.
"""
# Generate most recent blog post
title = Blog.objects.all()
post_date = Blog.objects.all()
get_absolute_url = Blog.objects.all()
# Render the HTML template index.html with the data in the context variable.
return render(
request,
'index.html',
context={'title': title,
'post_date': post_date,
'get_absolute_url': get_absolute_url,
}
)
def recent_blog_posts.html(request):
blog = Blog.objects.order_by('-post_date')[0:11]
return render(request, 'index.html', {'blog': blog})
class BlogListView(generic.ListView):
"""
Generic class-based view for a list of blog posts.
"""
model = Blog
paginate_by = 20
def get_queryset(self):
return Blog.objects.order_by('-post_date')
class BlogDetailView(generic.DetailView):
"""
Generic class-based detail view for a blog post.
"""
model = Blog
def bio(request):
return render(
request, 'bio.html'
)
def resume(request):
return render(
request, 'resume.html'
)
index.html
<div>
<h2>Recent Blog Posts</h2>
<div>
{% block blogs %}
{% if blog_list %}
{% for blog in blog_list %}
<article>
<header>
<h4><small>{{blog.post_date}} ยป </small>{{ blog.title }}</h4>
</header>
</article>
{% endfor %}
{% else %}
<p>Unfortunately, there are no blog posts yet.</p>
{% endif %}
</div>
{% endblock %}
</div>
<div>
<h2>Portfolio</h2>
{% if portfolio %}
{% for portfolio in portfolio %}
<div class="col-xs-12 col-sm-6 thumb">
<a class="thumbnail" href="{{ portfolio.get_absolute_url }}">
{% load static %}
<img class="img-responsive" src="{{ portfolio.cover_image }}" alt="">
<p>{{ portfolio.title }}</p>
<p>{{ portfolio.client_name }}</p>
</a>
</div>
{% endfor %}
{% else %}
<div>
<p>Unfortunately, there are no portfolio pieces yet.</p>
</div>
{% endif %}

You must use the variable pass to your context, in your template.
So you have to pass the variable blog_list to your context to iterate on it in your template.
Example below: (only for blog_list)
def index(request):
"""
View function for home page of site.
"""
blog_list = Blog.objects.all()
# Render the HTML template index.html with the data in the context variable.
return render(request, 'index.html', context={'blog_list': blog_list})

You need to pass the data to the template in the context. I don't really understand what you're trying to do in the index by setting and passing the variables in the context:
# Generate most recent blog post
title = Blog.objects.all()
post_date = Blog.objects.all()
get_absolute_url = Blog.objects.all()
Blog.objects.all() returns a queryset with all blog instances, not a title/post_date/get_absolute_url of a single blog instance.
In the template you refer to two context variables: blog_list and portfolio. You don't set the variables in the index. I would also avoid a statement like that:
{% for portfolio in portfolio %}
Don't use the same variable name as the variable you iterate over, change the latter to portfolio or portfolio_list.
To make it work you might try this:
index.py
def index(request):
blog_list = Blog.objects.all()
portfolio_list = Portfolio.objects.all()
return render(request, 'index.html',
context={'blog_list': blog_list, 'portfolio_list': portfolio_list}
)
In the index.html file change:
{% for portfolio in portfolio %}
to
{% for portfolio in portfolio_list %}

Related

Django. Objects from several models, how to correct display url?

I have two models. I do filtering of objects by these models and display them in a template.
Everything works fine, but an error occurs with url.
my urls.py
path('objects_model_A/<int:objects_A_id>', views.objects_A, name='objects_model_A'),
path('objects_model_B/<int:objects_B_id>', views.objects_B, name='objects_model_B'),
my views.py
def index(request):
objects_A = objects_A.objects.all().filter(is_published=True)
objects_B = objects_B.objects.all().filter(is_published=True)
queryset_list = list(chain(objects_A, objects_B))
context = {'queryset_list': queryset_list}
return render(request, 'templates/index.html', context)
def objects_A(request, objects_A_id):
objects_A = get_object_or_404(objects_a, pk=objects_A_id)
context = {
'objects_A': objects_A
}
return render(request, 'templates/objects_A.html', context)
def objects_B(request, objects_B_id):
objects_B = get_object_or_404(objects_b, pk=objects_B_id)
context = {
'objects_A': objects_A
}
return render(request, 'templates/objects_B.html', context)
my template.html
{% if queryset_list %}
{% for listing in queryset_list %}
<div class="col-md-6 col-lg-4 mb-4">
<div>
<a href="{% url 'objects_model_A' listing.id %}">Link
</a>
{% endfor %}
{% endif %}
Objects from different models are collected, have an appropriate data set, but url are wrong.
The object with model_A, url:
http://127.0.0.1:8000/objects_A/1
An object with model_B, url too:
http://127.0.0.1:8000/objects_A/1
I understand the error in the template. Line <a href="{% url 'objects_model_A' listing.id %}.
How to draw up URLs correctly so that objects from different models in the chain are displayed correctly. For object A was url:
http://127.0.0.1:8000/objects_A/1
For object B was url:
http://127.0.0.1:8000/objects_B/1
One often specifies a canonical URL at the model level with a get_absolute_url method [Django-doc]. You can thus implement this like:
from django.urls import reverse
class objects_A(models.Model):
# ...
def get_absolute_url(self):
return reverse('objects_model_A', kwargs={'objects_A_id': self.pk})
class objects_B(models.Model):
# ...
def get_absolute_url(self):
return reverse('objects_model_B', kwargs={'objects_B_id': self.pk})
In your template, you can then use:
{% for listing in queryset_list %}
<div class="col-md-6 col-lg-4 mb-4">
<div>
Link
</div>
</div>
{% endfor %}
Django has some tooling that takes get_absolute_url into account. For example you can pass a model object with a get_absolute_url method to a redirect(..) call [Django-doc], and Django will then redirect to the get_absolute_url() result of that object.

Django: how to get the second last object from database?

I have a model for news. I add news to the database with Django admin. Model Post consists of title, body and image.
On the main.html page of my project i have a carousel with 3 slides.
I already have some news in my database, and I want to display the last, the second last and so on images in that carousel.
My question is: what code should I add in html to display the last, the second last and the third last images?
<img src="???"> {# the last image #}
<img src="???"> {# the second last image #}
<img src="???"> {# the third last image #}
You can try like this:
posts= Post.objects.all().order_by('-id')
last_post = posts[0]
second_last_post = posts[1]
third_last_post = posts[2]
last_three_posts = posts[0:3]
But make sure to have atleast three posts or else it will throw index error
# views.py
from django.views.generic import TemplateView
class HomePageView(TemplateView):
template_name = 'home.html'
posts = Post.objects.all().order_by('-id')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context = {
'carousel_posts': self.posts[:3],
}
return context
Use this make a for loop over the carouser_posts keyword in your template and extract the information you need.
<div class="carousel-inner">
{% for post in carousel_posts %}
<div class="carousel-item active">
<img src="{{post.image}}">
<p>{{post.title}}"</p>
</div>
{% endfor %}
</div>
UPDATE
Answer to your update. Using context keyword carousel_posts provided by our HomePageView we can access post objects one by one using for loop.
In template, assuming that your Post model has an image field called image.
{% for post in carousel_posts %}
<img src="{{ post.image.url }}">
{% endfor %}

Combining two queries from different application/models to display them on a single overview page

I have only recently started working with Django and was wondering how one would go around to combine two queries from different application/models and display them in a given overview page. I will display some non-functional pseudo-code below to illustrate what I am trying to do:
Index.html
Note that I added two seperate context_object_names here just to illustrate what I am trying to do (latest_news and latest_enzyme)
{% extends 'base.html' %}
{% block body %}
<div id="docs-content">
<div class="section" id="s-module-django.db.models.fields">
<div class="page-header text-primary">
<h3 class="info-header-blue-publication-small">Overview</h3>
</div>
<div>
<h3 class="info-header-blue-publication-tiny">Latest news:</h3>
</div>
{% if latest_news %}
{{ latest_news.news_text }}
{% else %}
<p>No recent news.</p>
{% endif %}
<div>
<h3 class="info-header-blue-publication-tiny">Latest enzyme:</h3>
</div>
{% if latest_enzyme %}
<ul>
<li>{{ latest_enzyme.barcode }}</li>
</ul>
{% else %}
<p>No enzymes are available.</p>
{% endif %}
</div>
</div>
{% endblock %}
Views.py
Note that this contains some commented lines that illustrate the method that I was trying but did not get working, as well as two seperate get_querysets to illustrate my intent.
from django.shortcuts import render from django.http import
HttpResponse from django.views import generic
from gts.models import Enzymes
from news.models import News
# Create your views here.
class IndexView(generic.ListView):
template_name = 'overview/index.html'
#context_object_name = 'latest_enzyme_news'
#def get_queryset(self):
# latest_enzyme = Enzymes.objects.order_by('-pub_date')[0]
# latest_news = News.objects.order_by('-pub_date')[0]
# return (latest_enzyme, latest_news)
context_object_name = 'latest_enzyme'
def get_queryset(self):
return Enzymes.objects.order_by('-pub_date')[0]
context_object_name = 'latest_news'
def get_queryset(self):
return News.objects.order_by('-pub_date')[0]
I have looked at similar questions, where they tried to combine multiple queries from multiple models of the same application (e.g. Display objects from different models at the same page according to their published date) but I would appreciate any hint or tip on what would be the 'best practice' for the situation that I described as I would wish to combine queries from different applications more often.
You don't want a ListView here at all. You're not listing things; you're just getting two separate items.
Rather, use a standard TemplateView and define get_context_data to return the specific items you want.
class IndexView(generic.TemplateView):
template_name = 'overview/index.html'
def get_context_data(self):
latest_enzyme = Enzymes.objects.order_by('-pub_date')[0]
latest_news = News.objects.order_by('-pub_date')[0]
return {'latest_enzyme': latest_enzyme, 'latest_news': latest_news}
(Note also, you could just as easily use a function-based view for this, since you are not really getting any value from the class.)

Multiple models generic DetailView to template

I have 2 models and I got the IndexView working properly using the get_context_data method. However my DetailView using the same technique is not working. How do I simply get 2 models into the DetailView?
views.py
from .models import CharacterSeries, CharacterUniverse
class IndexView(generic.ListView):
template_name = 'character/index.html'
context_object_name = 'character_series_list'
def get_queryset(self):
return CharacterSeries.objects.order_by('name')
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['character_universe_list'] = CharacterUniverse.objects.order_by('name')
return context
class DetailView(generic.DetailView):
model = CharacterSeries
template_name = 'character/detail.html'
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
context['character_universe_list'] = CharacterUniverse.objects.all()
return context
I am missing something; I need to get CharacterUniverse into the DetailView. I have tried using this page's information to no avail.
Thanks all.
UPDATE:
detail.html
<ul>
{% for series in characterseries.character_set.all %}
<li>{{ series.name }}</li>
{% endfor %}
</ul>
<ul>
{% for universe in characteruniverse.character_set.all %}
<li>{{ universe.name }}</li>
{% endfor %}
</ul>
index.html
{% load staticfiles %}
<link rel ="stylesheet" type="text/css" href="{% static 'character/style.css' %}" />
<h1>Character Series</h1>
<ul>
{% for character_series in character_series_list %} {# for MODEL in .. #}
<li>{{ character_series.name }}</li>
{% endfor %}
</ul>
<h1>Character Universe</h1>
<ul>
{% for character_universe in character_universe_list %} {# for MODEL in .. #}
<li>{{ character_universe.name }}</li>
{% endfor %}
</ul>
An example from the official documentation: This is not possible in my case?
from django.views.generic import DetailView
from books.models import Publisher, Book
class PublisherDetail(DetailView):
model = Publisher
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(PublisherDetail, self).get_context_data(**kwargs)
# Add in a QuerySet of all the books
context['book_list'] = Book.objects.all()
return context
Perhaps this is the final solution? This is not working though.. Naming wrong?
ALL THE UPDATES
views.py
from django.shortcuts import get_object_or_404, render
from django.views import generic
from django.views.generic import DetailView
from .models import CharacterSeries, CharacterUniverse
class IndexView(generic.ListView):
template_name = 'character/index.html'
context_object_name = 'character_series_list'
def get_queryset(self):
return CharacterSeries.objects.order_by('name')
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['character_universe_list'] = CharacterUniverse.objects.order_by('name')
return context
class SeriesDetail(DetailView):
model = CharacterSeries
template_name = 'character/series_detail.html'
class UniverseDetail(DetailView):
model = CharacterUniverse
template_name = 'character/universe_detail.html'
urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>[0-9]+)/$', views.SeriesDetail.as_view(), name='series_detail'),
url(r'^(?P<pk>[0-9]+)/$', views.UniverseDetail.as_view(), name='universe_detail'),
]
index.html
{% load staticfiles %}
<link rel ="stylesheet" type="text/css" href="{% static 'character/style.css' %}" />
<h1>Character Series</h1>
<ul>
{% for character_series in character_series_list %}
<li>{{ character_series.name }}</li>
{% endfor %}
</ul>
<h1>Character Universe</h1>
<ul>
{% for character_universe in character_universe_list %}
<li>{{ character_universe.name }}</li>
{% endfor %}
</ul>
series_detail.html
<ul>
{% for series in characterseries.character_set.all %}
<li>{{ series.name }}</li>
{% endfor %}
</ul>
universe_detail.html
<ul>
{% for universe in characteruniverse.character_set.all %}
<li>{{ universe.name }}</li>
{% endfor %}
</ul>
Explanation of why you can't use one view for both models
A DetailView is meant to display details about an object from one model. It's fine to include extra context, but the view isn't designed to handle two possible models.
The example from the docs is showing the details for one publisher, and displaying all the books at the same time.
Your DetailView lets you show the details for one CharacterSeries, and display all of the CharacterUniverse at the same time.
However, you cannot use that same view to display details for one CharacterUniverse. You need a different view to display details for one CharacterUniverse
Solution
Therefore, you need two different detail views, one for each model.
You need a distinct url for each view. Otherwise, the request will always match the first regex (in this case series_detail. The following would work.
url(r'^series/(?P<pk>[0-9]+)/$', views.SeriesDetail.as_view(), name='series_detail'),
url(r'^universe/(?P<pk>[0-9]+)/$', views.UniverseDetail.as_view(), name='universe_detail'),

Django class based views with url issue

I'm trying to get to grips with class based views.
I have urls.py as follows:
urlpatterns = patterns('homework.views',
(r'^index/$', 'index'),
url(r'^(?P<sub_slug>\w+)/$', NavListView.as_view(), name='nav'),
url(r'^(?P<sub_slug>\w+)/(?P<class_grp_slug>\w+)/$', SubNavListView.as_view(), name='subnav'),
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),)
I have my views.py:
# Subject navigation
class NavListView(ListView):
template_name = 'templates/home.html'
def get_queryset(self):
self.subject = Subject.objects.all()
return self.subject
def get_context_data(self, **kwargs):
context = super(NavListView, self).get_context_data(**kwargs)
context['subjects'] = self.subject
return context
# Class group navigation
class SubNavListView(NavListView):
def get_queryset(self):
self.group = Group.objects.filter(subject__name__iexact=self.kwargs['sub_slug'])
return self.group
def get_context_data(self, **kwargs):
context = super(NavListView, self).get_context_data(**kwargs)
context['groups'] = self.group
return context
In my 'templates/home.html' I have:
{% extends 'templates/base.html' %}
{% load url from future %}
{% block nav-menu-items %}
<ul class="nav">
{% for sub in subjects %}
<li class="">{{ sub }}</li>
{% endfor %}
<li class="active">Add Subject</li>
</ul>
{% endblock nav-menu-items %}
{% block class_groups_nav %}
<div class="tabbable">
<ul class="nav nav-tabs">
{% for group in groups %}
<li>
<a data-toggle="tab" href="{% url 'subnav' sub_slug class_grp_slug %}">{{ group }}</a>
</li>
{% endfor %}
<li>Add</li>
</ul>
{% endblock class_groups_nav %}
I'm trying to achieve a 'nav' of subjects, then a 'subnav' below showing a tab for each class group for the subject selected in the navigation above.
I've tried different ways of doing this such as making Subject.objects.all() available as context processors. I have also attempted to subclass NavListView so I can inherit the previous context, making them available in SubNavListView.
At the moment, I'm getting a NoReverseMatch error where the url named 'nav' is not passing the sub_slug and so I can't use it in the url in the template.
Any thoughts on getting this working?
Many thanks,
Assuming your Subject model has field named slug in it, you need to update your code to
<li class="">{{ sub }}</li>
ie. pass an appropriate parameter to {%url ... %}. Change sub.slug to whatever field name you want to refer to.
If you want to, you can also do {% url 'nav' sub_slug=sub.slug %}.
You are trying to pass sub_slug, but which is not defined in the template context, and result in empty string. So nav url will not get any parameter.
I see similar problem in your other {%url ...%} tags in the template.