Django complex QuerySet ManyToManyField with other ManyToManyField - django

Sorry for this title, I wasn't sure how to name it properly. I'm having problem with getting queryset of ManyToManyField that is in relation with other ManyToManyField. So it's look like this, there is model Company that has ManyToManyField with Person and Person model got ManyToManyField with Position, because logic behind it is that 1 company can have many persons and 1 person can have few positions and can be employed by few companies (which is clear I think). I'm getting the queryset for Person in Company by this way
{% for team in brand.team.all %}
<p>{{ team.first_name }} {{ team.last_name }}</p>
<img class="img-thumbnail" src="/media/{{ team.photo }}">
<p>{{ team.position }} </p>
<p>{{ team.about }} </p>
{% endfor %}
And I'm getting what I want, comparing this to template looks like this
but I'm not getting positions of person, only company.Position.None and I've no idea how to get this. From documentation so far I know that it works for single ManyToManyField but I couldn't find example similar to mine case and I'm not sure how I should get (person's position)
Below are my files
models.py
from django.db import models
...
TYPES = (
('Startup', 'Startup'),
... )
CITIES = (
('Warszawa', 'Warszawa'),
... )
STACK = (
('PHP', 'PHP'),
... )
STUDENTS = (
('No', 'No'),
('Yes', 'Yes')
)
STACK_ICONS = (
('/static/icons/stack/php.png', 'PHP'),
('/static/icons/stack/javascript.png', 'JavaScript'),
...
)
POSITIONS = (
('CEO', 'Chief Executive Officer'),
('CTO', 'Chief Technology Officer'),
...
)
# object position with relationship many to many to person
class Position(models.Model):
position = models.CharField(max_length=50, choices=POSITIONS)
def __str__(self):
return self.position
# object person relation many to one (many persons to one company)
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
about = models.TextField(max_length=500, default=None)
position = models.ManyToManyField('Position')
photo = models.ImageField(blank=True, null=True, default=None)
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
# object company
class Company(models.Model):
# field person with relation many to one (many persons to 1 company)
team = models.ManyToManyField('Person')
name = models.CharField(max_length=100, blank=False)
technologies = models.ManyToManyField('Stack')
website = models.TextField(max_length=150, blank=True, null=True, validators=[URLValidator()])
...
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Company, self).save(*args, **kwargs)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.name
# object stack relation manytomany with Company
class Stack(models.Model):
name = models.CharField(max_length=30, choices=STACK)
icon = models.CharField(max_length=80, choices=STACK_ICONS)
def __str__(self):
return self.name
views.py
from django.shortcuts import render, get_object_or_404, redirect
...
def brands(request, slug):
brand = get_object_or_404(Company, slug=slug)
return render(request, 'company/comp_view.html', {'brand': brand})
def stacks(request):
stack = get_object_or_404(Stack)
return render(request, 'company/comp_view.html', {'stack': stack})
def positions(request):
position = get_object_or_404(Position)
return render(request, 'company/comp_view.html', {'position': position})
...
comp_view.html
{% extends 'company/base.html' %}
<div class="col col-md-1"></div>
<div class="col col-md-5 card-section">
<div class="team ">
<div class="card-title">
<span>Team</span>
</div>
<div class="row text-center">
<div class="col col-md-4">
{% for team in brand.team.all %}
<p>{{ team.first_name }} {{ team.last_name }}</p>
<img class="img-thumbnail" src="/media/{{ team.photo }}">
<p>{{ team.position }}</p>
<p>{{ team.about }} </p>
</div>
{% endfor %}
</div>
</div>
{% endblock %}

There's no such thing as a "single ManyToManyField". You have an m2m relationship, you need to iterate through it just like you do for the team members.
{% for position in team.position.all %}
{{ position.name }}
{% endfor %}

Related

How to query from 2 Models in Class based views in django?

I have a model Log and another model Solutions and I am using DetailView to display details of each log
Each log can have many solutions.
There is a log field in the Solutions model that is Foreign Key to Log model..
Now how do I access both Log model and Solutions of that particular log in the same html template if I want to display all the solutions of that particular log below the details of the log
models.py:
class Log(models.Model):
title = models.CharField(blank=False, max_length=500)
content = models.TextField(blank=False)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
slug = models.SlugField(max_length=50, null=False, unique=True)
author = models.ForeignKey(User, on_delete=models.CASCADE,null=True, blank=True)
image = models.ImageField(
upload_to='images', blank=True)
def save(self, *args, **kwargs):
super().save()
self.slug = self.slug or slugify(self.title + '-' + str(self.id))
super().save(*args, **kwargs)
class Meta:
verbose_name = ("Log")
verbose_name_plural = ("Logs")
def __str__(self):
return f"{self.title}"
def get_absolute_url(self):
return reverse("log-detail", kwargs={"question": self.slug})
class Solutions(models.Model):
log = models.ForeignKey(
Log, on_delete=models.CASCADE, blank=True, null=True)
author = models.ForeignKey(User, on_delete=models.CASCADE,null=True, blank=True)
solution = models.TextField(null=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
slug = models.SlugField(max_length=50, null=False, blank=True)
image = models.ImageField(
upload_to='images', blank=True)
def save(self, *args, **kwargs):
self.slug = self.slug or slugify(self.solution)
super().save(*args, **kwargs)
class Meta:
verbose_name = ("Solution")
verbose_name_plural = ("Solutions")
def __str__(self):
return f" {self.solution} "
views.py:
class LogDetailView(DetailView):
model = Log
slug_url_kwarg = 'question'
slug_field = 'slug'
log_detail.html:
{% extends 'log/base.html' %}
{%load crispy_forms_tags %}
{% block content %}
<title>Error Logger - {{object.title}}</title>
<div class="main container mt-4 p-3 mb-4">
<img style='display:inline;' class='rounded-circle account-img' src="{{ object.author.profile.avatar.url }}" alt="">
<h1 style='display:inline;'>
{{ object.title }}
</h1>
<p>Author: {{ object.author }}</p>
<p>Date and time of creation: {{ object.created }}</p>
<span> Details </span>:
<p class="big ml-4">{{ object.content }} <br />
{% if object.image %}
<img style="width: 20vw" class="mt-4" src="{{ object.image.url }}" alt="image" />
{% else %}
{% endif %}
</p>
</div>
<br />
<a
class="btn btn-outline btn-info button-solution"
href="#"
>Add solution</a
>
You can enumerate over these in the template by accessing the relation in reverse, this is normally modelname_set, unless you set a related_name=…. So in this case it is solutions_set:
{% for solution in object.solutions_set.all %}
{{ solution }}
{% endfor %}
If the ForeignKey has a related_name=… [Django-doc], for example with:
class Solutions(models.Model):
log = models.ForeignKey(
Log,
on_delete=models.CASCADE,
blank=True,
null=True,
related_name='solutions'
)
# …
Then we access this with:
{% for solution in object.solutions.all %}
{{ solution }}
{% endfor %}
Note: normally a Django model is given a singular name, so Solution instead of Solutions.

Trying to show latest record - Django

Models
class Category(models.Model):
class Meta():
verbose_name_plural = "Categories"
cat_name = models.CharField(max_length=50)
description = models.TextField()
def get_forums(self):
get_forum = Forum.objects.filter(category=self)
return get_forum
def __str__(self):
return f"{self.cat_name}"
class Forum(models.Model):
class Meta():
verbose_name_plural = "Forums"
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="forums")
parent = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE)
forum_name = models.CharField(max_length=50)
description = models.TextField()
def __str__(self):
return f"{self.forum_name}"
class Thread(models.Model):
class Meta():
verbose_name_plural = "Threads"
get_latest_by = "date_posted"
forum = models.ForeignKey(Forum, on_delete=models.CASCADE, related_name="threads")
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=50)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return f"{self.title} by: {self.author}"
View
class Home(ListView):
model = Category
template_name = 'forums/index.html'
context_object_name = 'category'
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
# Add in a QuerySet of all the Cat
context['category'] = Category.objects.all()
return context
HTML
{% block content %}
{% for cat in category %}
<div style="padding-top: 20px;">
<div class="row">
<div class="bg-success rounded-top border border-dark" style="width:100%; padding-left:8px;">
{{ cat.cat_name }}
</div>
</div>
{% for forum in cat.forums.all %}
<div class="row">
<div class="bg-secondary border border-dark" style="width:100%; padding-left:16px;">
{{ forum.forum_name }}
{% for threads in forum.threads.all %}
<div class="float-right" id="latest-post">
<p>{{ threads.title }}</p>
<p> {{ threads.author.username }} </p>
</div>
{% endfor %}
</div>
</div>
{% endfor%}
</div>
{% endfor %}
{% endblock content %}
Problem
I am building a forums and am trying to get my homepage to show the last post in a forum.
I have got it to work to show all threads, but i just want the latest one to show on the latest post div.
I setup the get_latest_by on the Thread model so that it gets the latest one by the time created.
How would i be able to get this to display the latest thread?
You can set a property on the Form model, and then call it in the template.
views.py
class Form(models.Model):
...
#property
def get_newest_thread(self):
return self.threads.all().order_by('date_posted').first()
html
{% with threads=forum.get_newest_thread %}
<div class="float-right" id="latest-post">
<p>{{ threads.title }}</p>
<p> {{ threads.author.username }} </p>
</div>
{% endwith %}

Django - Models: for loop in query/filter

I have a list of reviews and each review has a rating average. My problem is trying to added each review id to the filter for the query result. For this, I assume a for loop in the filter is best.
I've found a previous post with a similar situation, but the same result doesn't seem to be working for me.
When I load my reviews page, I receive a TypeError: 'function' object is not iterable.
Here is my view.py file with the queries.
def reviews(request):
context = {
'reviews': Employee.objects.all(),
'rating': Employee.objects.filter(id__in=[review.id for review in reviews]).aggregate(rate=Avg(F('value1')+F('value2')+F('value3').....+F('valueN'))/N)
}
return render(request, 'reviews/reviews.html', context)
Reviews.html template.
{% extends "reviews/layout.html" %}
{% block content %}
{% for review in reviews %}
{% for rating in ratings %}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{ review.author.profile.image.url }}">
<div class="media-body">
<div class="article-metadata">
<h4 class="mr-2">{{ review.company }} {{rating}}</h4>
<small class="text-muted">{{ review.date_posted|date:"F d, Y" }}</small>
</div>
<h5><a class="article-title" href="{% url 'review-detail' review.id %}">{{ review.title }}</a></h5>
<p class="article-content">{{ review.content }}</p>
</div>
</article>
{% endfor %}
{% endfor %}
{% endblock content %}
Any suggestions are much appreciated.
EDIT: Here is my model for the Employee table.
class Employee(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
company = models.CharField(max_length=100)
recommend = models.BooleanField(default=False)
salary = models.CharField(max_length=100)
salary_satis = models.CharField(max_length=100)
culture = models.CharField(max_length=100)
location = models.CharField(max_length=100)
work_env = models.CharField(max_length=100)
communication = models.CharField(max_length=100)
opportunity = models.CharField(max_length=100) # Opportunity happiness
leadership_satis = models.CharField(max_length=100)
fair_treatment = models.CharField(max_length=100)
advice = models.TextField() # Advice for management
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
ordering = ['-date_posted']
def __str__(self):
return f'{self.title, self.content, self.company, self.recommend, self.salary, self.salary_satis, self.culture, self.location, self.work_env, self.communication, self.opportunity, self.leadership_satis, self.fair_treatment, self.advice, self.date_posted, self.author}'
def get_absolute_url(self):
# Returns user to reviews page after their review has been submitted.
return reverse('reviews')
Your reviews in the dictionary, refers to the reviews view function (here both in boldface):
def reviews(request):
context = {
'reviews': Employee.objects.all(),
'rating': Employee.objects.filter(
id__in=[review.id for review in reviews]
).aggregate(
rate=Avg(F('value1')+F('value2')+F('value3').....+F('valueN'))/N
)
}
return render(request, 'reviews/reviews.html', context)
Indeed, you can not iterate over this function, since it has no __iter__ method attached to it, but even if it had, this is not what you want to do.
I think the most elegant way to solve this, is simply defining a reviews variable:
def reviews(request):
reviews = Employee.objects.all()
context = {
'reviews': reviews,
'rating': Employee.objects.filter(
id__in=[review.id for review in reviews]
).aggregate(
rate=Avg(F('value1')+F('value2')+F('value3').....+F('valueN'))/N
)
}
return render(request, 'reviews/reviews.html', context)
So now reviews is a local variable that you can acces, and since a queryseet is iterable, we can iterate over this.
That being said, it is a bit odd to use an id__in here, since reviews has all the Employees.

Django - Custom ModelMultipleChoiceField can't categorize choices based on their parent model

EDITS AVAILABLE BELOW!
My goal:
Category1
----Option1
----Option2
--Option3
Category2
----Option1
----Option2
etc.
I have a parent model (Venue) and a child model (Amenity). A venue can have many amenities.
while configuring my initial data and presenting it with {{form.as_p}} everything works as expected.
But when I try to render my own custom form, so that I can apply a loop, It doesn't pre-populate them.
Here is my template:
<form method="POST" class="ui form">
{% csrf_token %}
{% for category in categories %}
<h4 class="ui horizontal divider header">
<i class="list icon"></i>
{{category.category}}
</h4>
<p class="ui center aligned text"><u>{{category.description}}</u></p>
{% for amenity in category.amenity_set.all %}
<div class="inline field">
<label for="choices_{{amenity.id}}"></label>
<div class="ui checkbox">
<input id="choices_{{amenity.id}}" type="checkbox" value="{{amenity.id}}" name="choices">
<label><span data-tooltip="{{amenity.description}}" data-position="top left">{{amenity}}</span></label>
</div>
</div>
{% endfor %}
{% endfor %}
<button type="submit" name="submit" class="ui button primary">Next</button>
</form>
my ModelForm:
class AmenitiesForm(ModelForm):
class Meta:
model = Venue
fields = ('choices',)
choices = forms.ModelMultipleChoiceField(Amenity.objects.all(), widget=forms.CheckboxSelectMultiple,)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if kwargs.get('instance'):
initial = kwargs.setdefault('initial', {})
initial['choices'] = [c.pk for c in kwargs['instance'].amenity_set.all()]
forms.ModelForm.__init__(self, *args, **kwargs)
def save(self, commit=True):
instance = forms.ModelForm.save(self)
instance.amenity_set.clear()
instance.amenity_set.add(*self.cleaned_data['choices'])
return instance
and my views.py:
class AddAmenitiesView(LoginRequiredMixin, CreateView):
"""
AddAmenitiesView is the view that prompts the user to select the amenities of their venue.
"""
model = Venue
form_class = AmenitiesForm
template_name = 'venues/add_amenities.html'
def parent_venue(self):
"""
returns the parent_venue based on the kwargs
:return:
"""
parent_venue = Venue.objects.get(id=self.kwargs["venue_id"])
return parent_venue
def get_initial(self):
initial = super().get_initial()
initial['choices'] = self.parent_venue().amenity_set.all()
return initial
def form_valid(self, form):
venue = Venue.objects.get(id=self.kwargs['venue_id'])
form.instance = venue
# form.instance.owner = self.request.user
return super().form_valid(form)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["parent_venue"] = self.parent_venue()
context["categories"] = AmenitiesCategory.objects.all()
return context
def get_success_url(self):
return reverse('add-amenities', kwargs={'venue_id': self.object.id,})
I suppose it has to do with my template since rendering the form normally, it does prepopulate the model.
Thank you for taking the time!
EDIT:
With Raydel Miranda's answer below I managed to edit the templates for how the form gets presented:
forms.py:
class CustomAmenitiesSelectMultiple(CheckboxSelectMultiple):
"""
CheckboxSelectMultiple Parent: https://docs.djangoproject.com/en/2.1/_modules/django/forms/widgets/#CheckboxSelectMultiple
checkbox_select.html: https://github.com/django/django/blob/master/django/forms/templates/django/forms/widgets/checkbox_select.html
multiple_input.html: https://github.com/django/django/blob/master/django/forms/templates/django/forms/widgets/multiple_input.html
checkbox_option.html: https://github.com/django/django/blob/master/django/forms/templates/django/forms/widgets/checkbox_option.html
input_option.html: https://github.com/django/django/blob/master/django/forms/templates/django/forms/widgets/input_option.html
"""
template_name = "forms/widgets/custom_checkbox_select.html"
option_template_name = 'forms/widgets/custom_checkbox_option.html'
class AmenitiesForm(ModelForm):
class Meta:
model = Venue
fields = ('choices',)
choices = forms.ModelMultipleChoiceField(Amenity.objects.all(), widget=CustomAmenitiesSelectMultiple,)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if kwargs.get('instance'):
initial = kwargs.setdefault('initial', {})
initial['choices'] = [c.pk for c in kwargs['instance'].amenity_set.all()]
forms.ModelForm.__init__(self, *args, **kwargs)
def save(self, commit=True):
instance = forms.ModelForm.save(self)
instance.amenity_set.clear()
instance.amenity_set.add(*self.cleaned_data['choices'])
return instance
custom_checkbox_select.html:
{% with id=widget.attrs.id %}
<div class="inline field">
<div {% if id %} id="{{ id }}" {% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}" {% endif %}>
{% for group, options, index in widget.optgroups %}{% if group %}
<div>
{{ group }}
<div>
{% if id %} id="{{ id }}_{{ index }}" {% endif %}>{% endif %}{% for option in options %}
<div class="checkbox">{% include option.template_name with widget=option %}</div>
{% endfor %}{% if group %}
</div>
</div>
{% endif %}{% endfor %}
</div>
</div>
{% endwith %}
custom_checkbox_option.html :
<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>{% endif %}{% include "django/forms/widgets/input.html" %}{% if widget.wrap_label %} {{ widget.label }}</label>
As requested, also my models.py:
class TimeStampedModel(models.Model):
"""
An abstract base class model that provides self-updating
"created" and "modified" fields.
"""
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class VenueType(TimeStampedModel):
type = models.CharField(max_length=250)
description = models.TextField()
def __str__(self):
return self.type
class Venue(TimeStampedModel):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=250)
type = models.ForeignKey(VenueType, on_delete=models.CASCADE)
total_capacity = models.PositiveIntegerField(default=0)
description = models.TextField(blank=False)
contact_number = PhoneNumberField(blank=True)
contact_email = models.EmailField(blank=True)
published = models.BooleanField(default=False)
def __str__(self):
return self.name
class AmenitiesCategory(TimeStampedModel):
category = models.CharField(max_length=250)
description = models.TextField()
def __str__(self):
return self.category
class Amenity(TimeStampedModel):
category = models.ForeignKey(AmenitiesCategory, on_delete=models.CASCADE)
venues = models.ManyToManyField(Venue, blank=True)
space = models.ManyToManyField(Space, blank=True)
name = models.CharField(max_length=250)
description = models.TextField()
def __str__(self):
return self.name
class Meta:
ordering = ['category']
You said while configuring my initial data and presenting it with {{form.as_p}} everything works as expected, if so, use {{ form.choices }} in order to render that field.
<form method="POST" class="ui form">
{% csrf_token %}
{{form.choices}}
<button type="submit" name="submit" class="ui button primary">Next</button>
</form>
Then, what you need is have a custom CheckboxSelectMultiple with its own template (in case you want a custom presentation to the user), and use it in your form:
Custom CheckboxSelectMultiple could be:
class MyCustomCheckboxSelectMultiple(CheckboxSelectMultiple):
template_name = "project/template/custom/my_checkbox_select_multiple.html"
And in the form:
class AmenitiesForm(ModelForm):
# ...
choices = forms.ModelMultipleChoiceField(Amenity.objects.all(), widget=forms.MyCustomCheckboxSelectMultiple)
# ...
How to implement the template my_checkbox_select_multiple.html, is up to you.
If you're using some Django prior to 1.11, visit this link to learn about a others things you've to do in order to customize a widget template.
Django widget override template
Hope this help!

Reverse Iterating over Foreign Key

I am trying the tutorial for Django on making a local library website with books.
One of the challenges is to make an author detail view which lists all the books that author has written. I am having trouble displaying any book information for the author.
Model.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
from django.urls import reverse
import uuid
# Create your models here.
class Genre(models.Model):
"""
Model resprenting the book genre
"""
name=models.CharField(max_length=200, help_text="Enter a book genre")
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
summary = models.TextField(max_length=1000, help_text="Enter a brief description")
isbn = models.CharField('ISBN', max_length=13, help_text="13 character ISBN field")
genre = models.ManyToManyField(Genre, help_text="Select a genre for this book")
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('book-detail', args=[str(self.id)])
def display_genre(self):
return ', '.join([genre.name for genre in self.genre.all()[:3] ])
display_genre.short_description = 'Genre'
class BookInstance(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text="Unique book number")
book = models.ForeignKey('Book', on_delete=models.SET_NULL, null=True)
imprint = models.CharField(max_length=200)
due_back = models.DateField(null=True, blank=True)
LOAN_STATUS = (
('d', 'Maintenance'),
('o', 'On loan'),
('a', 'Available'),
('r', 'Reserved'),
)
status = models.CharField(max_length=1, choices=LOAN_STATUS, blank=True, default='d', help_text="Book Availability")
class Meta:
ordering = ['due_back']
def __str__(self):
return ('%s (%s)' %(self.id, self.book.title))
class Author(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.CharField(max_length=10, null=True, blank=True)
date_of_death = models.CharField(max_length=10, null=True, blank=True)
def get_absolute_url(self):
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
return ('%s, %s' % (self.last_name, self.first_name))
def display_books(self):
books = Book.objects.get(pk=self.id)
return books.book_set.all()
display_books.short_description = 'Books'
author_detail.html
Really lost here!
{% extends "base.html" %}
{% block content %}
<h1>Author: {{ author }}</h1>
<p><strong>Date of Birth:</strong> {{ author.date_of_birth }}</p>
<p><strong>Date of Death:</strong> {{ author.date_of_death }}</p>
<!--<p><strong>Books:</strong> {% for books in book.author.pk.all %} {{ book.author }}{% if not forloop.last %}, {% endif %}{% endfor %}</p> -->
<div style="margin-left:20px;margin-top:20px">
<h4>Books</h4>
{% for books in book.all %}
<p>{{ books }} Testing Vars {{ book.author_set }} Get copies from key {{ book.author.pk }} </p>
{% endfor %}
</div>
{% endblock %}
Views.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render
from django.views import generic
# Create your views here.
from .models import Book, Author, BookInstance, Genre
def index(request):
num_books=Book.objects.all().count()
num_instances=BookInstance.objects.all().count()
#available books
num_instances_available=BookInstance.objects.filter(status__exact='a').count()
num_authors=Author.objects.count()
return render(request,
'index.html',
context={'num_books': num_books, 'num_instances': num_instances,
'num_instances_available' : num_instances_available, 'num_authors': num_authors},
)
class BookListView(generic.ListView):
model = Book
class BookDetailView(generic.DetailView):
model = Book
class AuthorListView(generic.ListView):
model = Author
class AuthorDetailView(generic.DetailView):
model = Author
Assuming author_detail.html is used in AuthorDetailView ListView, I'd suggest some changes in your template, maybe like this,
{% block content %}
<h1>Author: {{ object.first_name }} {{ object.last_name }}</h1>
<p><strong>Date of Birth:</strong> {{ object.date_of_birth }}</p>
<p><strong>Date of Death:</strong> {{ object.date_of_death }}</p>
<div style="margin-left:20px;margin-top:20px">
<h4>Books</h4>
{% for book in object.book_set.all %}
<p>{{ book.title }}</p>
{% endfor %}
</div>
{% endblock %}
Since, you haven't overridden the get_context_data() method of the ListView, the default context_variable_name would be object. You may need to refer the Author instance by object.