I'm having problem with Django templates not receiving any data in image.url when having more than one object in context QuerySet all other data is working fine. If there is only one object in QuerySet, image.url works fine. I'm storing images on S3 and there is nothing wrong with the images or the bucket permission.
I'm new with Django templates is there anything I'm missing?
Here is the code:
models.py
`
from django.db import models
from django.core.validators import MaxValueValidator, MinValueValidator
from django.utils.translation import gettext_lazy as _
from site_content.models import PageCategory, Page
from site_content.utils import file_upload_to
class PpcOffersPagesManager(models.Manager):
def get_queryset(self):
return super(PpcOffersPagesManager, self) \
.get_queryset() \
.filter(page_category__category_name="ppc-offer-page")
class PpcOfferPages(Page):
objects = PpcOffersPagesManager()
class Meta:
proxy = True
def __str__(self):
return self.page_name
def save(self, *args, **kwargs):
self.page_category = PageCategory.objects.get(category_name="ppc-offer-page")
self.page_site_type = "ppc"
super(PpcOfferPages, self).save(*args, **kwargs)
class PpcOfferContent(models.Model):
related_page = models.ForeignKey(
Page, verbose_name=_("Page"),
related_name="ppc_offer_content",
on_delete=models.SET_NULL,
null=True
)
order = models.IntegerField(verbose_name=_("Order of offer appearance"), default=0)
image = models.ImageField(
verbose_name=_("Casino banner image"),
upload_to=file_upload_to,
null=True,
blank=True,
help_text="Casino banner image, Size (300x500)"
)
header = models.CharField(
verbose_name=_("Header text"),
max_length=500,
null=False,
blank=False
)
is_active = models.BooleanField(verbose_name=_("Is offer active"), default=True)
class Meta:
verbose_name = _('PPC_Offer Page Content')
verbose_name_plural = _('PPC_Offer Page Content')
def __str__(self):
return self.related_page.page_name
`
views.py
`
from django.views.generic import DetailView
from ppc_site_control.models import PpcOfferContent
class PpcOffersDetailView(DetailView):
model = PpcOfferContent
def get_template_names(self):
page = self.model.objects \
.filter(related_page__slug = self.kwargs['slug']) \
.first().related_page.page_template.template_path
return page
def get_object(self):
return self.model.objects \
.filter(related_page__slug=self.kwargs['slug']) \
.filter(is_active=True) \
.order_by('offer_order')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
return context
`
.html
`
{% extends "slotsselection/base.html" %}
{% load i18n static reviews sstags %}
{% for offer in object %}
<div>
<div >
<img loading="lazy" src="{{offer.offer_image.url}}" alt="image">
</div>
<div>
<p>{{offer.header}}</p>
<p>{{offer.is_active }}</p>
</div>
<div>
{% endfor %}
`
I tried sending data in the context as new key and adding QuerySet object again with .values()(to exclude possibility of problem with "get_object" query) still have the same result. When I remove the images from template, all other data is displayed accordingly, without images.
I also tried with ListView same error
ValueError The 'image' attribute has no file associated with it.
Data in context:
`{'object': <QuerySet [<PpcOfferContent: PPC PageName>,
<PpcOfferContent: PPC PageName>, <PpcOfferContent: PPC PageName>,
<PpcOfferContent: PPC PageName>]>, 'view':
<ppc_site_control.views.PpcOffersDetailView object at 0x7f2d8230f430>}
`
The problem was caused from missing images because they were not required in the model the simplest solution is adding "if" condition in the template:
`{% if offer.offer_image %}
<img loading="lazy" src="{{offer.image.url}}" alt="Casino image">
{% endif %}`
I also manage to find workaround:
views.py
`from slotsselection.components.s3_settings import MEDIA_URL`
then in the context data I added
`def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['media_url'] = MEDIA_URL
return context
`
.html
and in html I removed .url to get the half of the link as string and added media_url
`<img loading="lazy" src="{{media_url}}{{offer.offer_image}}" alt="Casino image">`
not best approach but it works to.
Related
I have a model for the Product Perfume, the product have different volume and prices.
I need to render the volume and price for each but i get "'function' object has no attribute 'prices'"
Any ideas? iam grateful for any suggestions
View:
from django.shortcuts import render
from django.views.generic import View, TemplateView
from products.models import Perfume, Pricing
def getIndex(request):
perfumes = Perfume.objects.all
thePrice= perfumes.prices.all()
return render(request, 'index.html', {'perfumes': perfumes, 'thePrice':thePrice})
Model
from django.conf import settings
from django.db import models
from django.utils import timezone
class Perfume(models.Model):
genderChoice = (
('unisex','unisex'), ('male', 'male'), ('female', 'female'))
name = models.CharField(max_length=50, default='')
brand = models.CharField(max_length=40, default='')
gender = models.CharField(max_length=7, choices=genderChoice, default='unisex')
description = models.TextField()
image = models.ImageField(upload_to='images/product_image')
created = models.DateField()
author =models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
active = models.BooleanField(default=False)
show_for_consumer = models.BooleanField(default=False)
def __str__(self):
return self.name
class Pricing(models.Model):
product =models.ForeignKey(Perfume, on_delete=models.CASCADE,related_name='prices')
price= models.DecimalField(max_digits=10, decimal_places=2)
volume= models.DecimalField(max_digits=10, decimal_places=2)
def __str__(self):
return 'Perfume {} - Price{} - Volume {}'.format(self.product.name,self.price, self.volume)
First, you need to correct the missing () in getting the perfumes queryset
perfumes = Perfume.objects.all()
This thePrice = perfumes.prices.all() is incorrect. You have to do this for every perfume, not for a queryset of perfumes.
Since Perfume and Pricing have a one-to-many relationship, you can directly access the prices from every perfume instance. This way you only need to pass the perfumes queryset
def getIndex(request):
perfumes = Perfume.objects.all()
return render(request, 'index.html', {'perfumes': perfumes})
Finally, in your template, you can call the prices like this
{% for product in perfumes %}
...
{% for p in product.prices.all %}
<option>{{p.volume}}ml - {{p.price}}$ </option>
...
{% endfor %}
{% endfor %}
Sorry for my poor English, I'm working around several days and I'm blocked, I clean my develop/debug database and after that my code that just didn't work any more.
I'm working for around to understand the reason and how to solve, but no clue. Ask for any path to go to solve this situation and help me.
I know I'm doing something wrong because only did:
./manage.py --fork contractos zero
clean all migrations data on contractos/migrations (except init.py)
./manage.py --fake
./manage.py makemigrations
./manage.py migrate
./manage.py runserver
<code>
my list.py
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.http import Http404
from django.shortcuts import render
# Create your views here.
from contractos.models import Contracto
def list(request):
if not request.user.is_staff and not request.user.is_superuser:
raise Http404
contractos_list = Contracto.objects.all() #.order_by("empresa")
paginator = Paginator(contractos_list, 10) # Show 25 contacts per page
page_request_var = 'lista'
page = request.GET.get(page_request_var)
try:
contractos = paginator.page(page)
except PageNotAnInteger:
#If page is not an integer, deliver first page.
contractos = paginator.page(1)
except EmptyPage:
#If page is out of range (e.g. 9999), deliver last page of results.
contractos = paginator.page(paginator.num_pages)
context = {
"object_list" : contractos,
"title": "Lista de Contractos",
"page_request_var": page_request_var,
}
return render(request, "contractos/list.html", context)
**my list.html**
{% extends "base.html" %}
{% block content %}
<div class="col-sm-9 col-sm-offset-1">
<h1>Contractos {{ title }}</h1>
<div class="row">
{% for obj in object_list %}
<div class="col-sm-6 col-md-4">
<div class="thumbnail">
<div class="caption">
<h3>
<a href='{{ obj.get_absolute_url }}'>{{ obj.empresa }} </a><br />
{% if obj.logo %}
<img src="{{ obj.logo.url }}" class="img-responsive"/>
{% endif %}
<small>criado {{ obj.timestamp|timesince }}</small><br />
<small>alterado em {{ obj.updated|timesince }}</small><br />
</h3>
{{ obj.nfic }} <br/>
{{ obj.descricao|linebreaks|truncatechars:60 }}
<p>...</p>
<p>Detalhes
<!--a href="#" class="btn btn-default" role="button">Button</a--></p>
</div>
</div>
</div>
{% cycle "" "<div class='col-sm-12'><hr/></div></div><div class='row'>" %}
{% endfor %}
</div>
<div class="pagination">
<span class="step-links">
{% if object_list.has_previous %}
Primeira
Anterior
{% endif %}
<span class="current">
{% if object_list.number %}
Pagina {{ object_list.number }} de {{ object_list.paginator.num_pages }}.
{% endif %}
</span>
{% if object_list.has_next %}
Seguinte
Ultima
{% endif %}
</span>
</div>
</div>
{% endblock content %}
**my models.py**
from __future__ import unicode_literals
import uuid
from django.db import models
from django.core.urlresolvers import reverse
STATUS_CHOICES = [
('a', 'Activo'),
('s', 'Suspenso'),
('c', 'Cancelado'),
('t', 'Terminado'),
]
def upload_location(instance, filename):
return "contractos/%s/%s" % (instance.nfic, filename)
# Create your models here.
class Contracto(models.Model):
slug = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
nfic = models.CharField(max_length=15, default='PT000000000', unique=True)
empresa = models.CharField(max_length=128, unique=True)
descricao = models.TextField()
status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='a')
logo = models.ImageField(upload_to=upload_location,
null=True,
blank=True,
width_field="width_field",
height_field="height_field"
)
height_field = models.IntegerField(default=0)
width_field = models.IntegerField(default=0)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
def __str__(self):
return self.empresa
def get_absolute_url(self):
return reverse("contractos:details", kwargs={"uuid": self.slug})
class Meta:
ordering = ["empresa"]
db_table = ["Contractos"]
and the error I got when try rendering list.py are:
**Environment:**
Request Method: GET
Request URL: http://127.0.0.1:8000/contractos/
Django Version: 1.10.4
Python Version: 3.5.1
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'contractos']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback:
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/core/paginator.py" in validate_number
34. number = int(number)
**During handling of the above exception (int() argument must be a string, a bytes-like object or a number, not 'NoneType'), another exception occurred:**
File "/home/gwo/Projects/paas/src/contractos/views/list.py" in list
20. contractos = paginator.page(page)
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/core/paginator.py" in page
50. number = self.validate_number(number)
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/core/paginator.py" in validate_number
36. raise PageNotAnInteger('That page number is not an integer')
**During handling of the above exception (That page number is not an integer), another exception occurred:**
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/core/paginator.py" in count
72. return self.object_list.count()
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/query.py" in count
369. return self.query.get_count(using=self.db)
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/query.py" in get_count
476. number = obj.get_aggregation(using, ['__count'])['__count']
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/query.py" in get_aggregation
457. result = compiler.execute_sql(SINGLE)
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/compiler.py" in execute_sql
824. sql, params = self.as_sql()
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/compiler.py" in as_sql
369. extra_select, order_by, group_by = self.pre_sql_setup()
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/compiler.py" in pre_sql_setup
46. self.setup_query()
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/compiler.py" in setup_query
36. self.query.get_initial_alias()
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/query.py" in get_initial_alias
879. alias = self.join(BaseTable(self.get_meta().db_table, None))
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/query.py" in join
921. alias, _ = self.table_alias(join.table_name, create=True)
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/query.py" in table_alias
689. alias_list = self.table_map.get(table_name)
**During handling of the above exception (unhashable type: 'list'), another exception occurred:**
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/core/handlers/exception.py" in inner
39. response = get_response(request)
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
187. response = self.process_exception_by_middleware(e, request)
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
185. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/gwo/Projects/paas/src/contractos/views/list.py" in list
23. contractos = paginator.page(1)
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/core/paginator.py" in page
50. number = self.validate_number(number)
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/core/paginator.py" in validate_number
39. if number > self.num_pages:
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/utils/functional.py" in __get__
35. res = instance.__dict__[self.name] = self.func(instance)
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/core/paginator.py" in num_pages
84. if self.count == 0 and not self.allow_empty_first_page:
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/utils/functional.py" in __get__
35. res = instance.__dict__[self.name] = self.func(instance)
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/core/paginator.py" in count
77. return len(self.object_list)
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/query.py" in __len__
238. self._fetch_all()
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/query.py" in _fetch_all
1087. self._result_cache = list(self.iterator())
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/query.py" in __iter__
54. results = compiler.execute_sql()
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/compiler.py" in execute_sql
824. sql, params = self.as_sql()
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/compiler.py" in as_sql
369. extra_select, order_by, group_by = self.pre_sql_setup()
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/compiler.py" in pre_sql_setup
46. self.setup_query()
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/compiler.py" in setup_query
36. self.query.get_initial_alias()
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/query.py" in get_initial_alias
879. alias = self.join(BaseTable(self.get_meta().db_table, None))
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/query.py" in join
921. alias, _ = self.table_alias(join.table_name, create=True)
File "/home/gwo/Projects/paas/lib/python3.5/site-packages/django/db/models/sql/query.py" in table_alias
689. alias_list = self.table_map.get(table_name)
**Exception Type: TypeError at /contractos/
Exception Value: unhashable type: 'list'**
</code>
</pre>
So I only clean my data from database to restart populate then again with test information to demonstrated the project to our customer, that as you can imagine I had to cancel!
You can't have a function called list because it's a python keyword. Change that to something else and it will work. Also you should avoid naming your file list etc.
I try to pass the variable to another page using GET method in django. It is possible for me to do that, but the problem is that the variable that I passed is not available in the if statement. I try to print out the value then it worked fine. Then I try to use it inside if statement then I come to know that it was not working properly. I have no idea regarding that. Can anyone help me? Thank you very much.
This is my views:
def test(request):
Test = Photos.objects.all()
ID = request.GET['id']
Context = {
'ID' : ID,
'test' : Test,
'testing' : 3,
}
return render(request, 'test.html', Context)
def tests(request):
tests = Photos.objects.all()
Context = {
'tests' : tests,
}
return render(request, 'tests.html', Context)
This is my urls.py:
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^$', 'flashapp.views.home'),
url(r'^play$', 'flashapp.views.play'),
url(r'^test$', 'flashapp.views.test'),
url(r'^tests$', 'flashapp.views.tests'),]
In tests.html I have buttons for passing id to test.html using GET method.
This is tests.html:
</head>
<body>
<h1>Welcome to tests pages.....</h1>
{% for i in tests %}
Click For {{ i.id }}<br>
{% endfor %}
</body>
This is test.html:
<html>
<head>
</head>
<body>
<h1>Welcome to Test Page</h1>
<h1>{{ ID }}</h1>
{% for i in test %}
<p>{{ i.id }}..........{{ testing }}.........{{ ID }}</p>
{% if ID == i.id %}
<p>Test</p>
<p>Working....ID = {{ i.id }}</p>
{% else %}
<p>In else</p>
{% endif %}
{% endfor %}
</body>
this is tests.html
this is test.html
I suppose to see the "Working....." but, it gone to else block. I have no idea. Help me please!!!
Thank you very much.
I edited my original answer based on your comment
You are passing a QuerySet as your Test object instead of an object in the first view, so it doesn't have an id property.
When you do this:
def test(request):
Test = Photos.objects.all()
...
You are getting a collection of all the Photos objects into the Test variable, which is not what you want, you only want one instance of Photos. For that kind of queries, you need to use the .get method, that returns a single instance or an exception in case it doesn't find it.
Test = Photos.objects.get(pk=request.GET['id'])
Your code now should look like this:
def test(request):
ID = request.GET['id']
Test = Photos.objects.get(pk=ID)
Context = {
'ID' : ID,
'test' : Test,
'testing' : 3,
}
return render(request, 'test.html', Context)
Now, for completeness' sake, this would fail in case the ID is not on the database, so we can do something like this:
def test(request):
try:
ID = request.GET['id']
Test = Photos.objects.get(pk=ID)
Context = {
'ID' : ID,
'test' : Test,
'testing' : 3,
}
return render(request, 'test.html', Context)
except Photos.DoesNotExist:
raise Http404("No Photo matches the given query.")
Of course, Django has its own shortcuts for these kind of things, so your code can be written like this:
from django.shortcuts import get_object_or_404
def test(request):
#I strongly suggest you don't use uppercase in variable names
id = request.GET['id']
test = get_object_or_404(pk=id)
context = {
'ID' : id,
'test' : test,
'testing' : 3,
}
return render(request, 'test.html', context)
I have this in my template file:
{% get_latest_show as slideshow %}
{% for slide in slideshow.slide_set.all %}
<img src="{% thumbnail slide.image 1174x640 upscale %}" alt="{{slide.title}}" width="1174"/>
{% endfor %}
models.py
from django.db import models
import datetime
class Slide(models.Model):
title = models.CharField(max_length=50)
description = models.TextField(blank=True, null=True)
target_url = models.TextField(blank=True, null=True)
slideshow = models.ForeignKey('Slideshow')
image = models.ImageField(upload_to='slideshow', max_length=500, blank=True,null=True)
def __unicode__(self):
return self.title
class Slideshow(models.Model):
title = models.CharField(max_length=50)
pub_date = models.DateTimeField(auto_now=True)
published = models.BooleanField(default=False)
class Meta:
ordering = ['-title']
def __unicode__(self):
return self.title
slide_tags.py
from django import template
from django.core.cache import cache
from django.contrib.contenttypes.models import ContentType
from slides.models import Slide, Slideshow
register = template.Library()
class GetSlideshowNode(template.Node):
"""
Retrieves the latest published slideshow
"""
def __init__(self, varname):
self.varname = varname
def render(self, context):
try:
show = Slideshow.objects.filter(published=True)[0]
except:
show = []
context[self.varname] = show
return ''
def get_latest_show(parser, token):
"""
Retrieves the latest published slideshow
{% get_latest_show as show %}
"""
args = token.split_contents()
argc = len(args)
try:
assert (argc == 3 and args[1] == 'as')
except AssertionError:
raise template.TemplateSyntaxError('get_latest_show syntax: {% get_latest_show as varname %}')
varname = None
t, a, varname = args
return GetSlideshowNode(varname=varname)
register.tag(get_latest_show)
The problem is that my slides are being displayed out of order. When I print slideshow.slide.set.all on the page, I see:
[<Slide: Slide 2>, <Slide: Slide 3>, <Slide: Slide 4>, <Slide: Slide 1>]
How do I get the slides to appear in order?
You want the slide_set to be ordered therefor the 'ordering' statement should be on the Slide model.
class Slide(models.Model):
# fields
class Meta:
ordering = ['-title']
This will cause Slide.objects.all() to return a queryset ordered by the title field. That is equivalent to slideshow.slide_set.all()
I am attempting to create a profile page that shows the amount of dwarves that are assigned to each corresponding career. I have 4 careers, 2 jobs within each of those careers and of course many dwarves that each have a single job. How can I get a count of the number of dwarves in each of those careers? My solution was to hardcore the career names in the HTML and to make a query for each career but that seems like an excessive amount of queries.
Here's what I "want" to see:
Unassigned: 3
Construction: 2
Farming: 0
Gathering: 1
Here's my models. I add some complexity by not connecting Careers directly to my Dwarves model (they have connected by their jobs).
from django.contrib.auth.models import User
from django.db import models
class Career(models.Model):
name = models.CharField(max_length = 64)
def __unicode__(self):
return self.name
class Job(models.Model):
career = models.ForeignKey(Career)
name = models.CharField(max_length = 64)
career_increment = models.DecimalField(max_digits = 4, decimal_places = 2)
job_increment = models.DecimalField(max_digits = 4, decimal_places = 2)
def __unicode__(self):
return self.name
class Dwarf(models.Model):
job = models.ForeignKey(Job)
user = models.ForeignKey(User)
created = models.DateTimeField(auto_now_add = True)
modified = models.DateTimeField(auto_now = True)
name = models.CharField(max_length = 64)
class Meta:
verbose_name_plural = 'dwarves'
def __unicode__(self):
return self.name
EDIT 1
my view looks something like:
def fortress(request):
careers = Career.objects.annotate(Count('dwarf_set'))
return render_to_response('ragna_base/fortress.html', {'careers': careers})
and template:
{% for career in careers %}
<li>{{ career.dwarf_set__count }}</li>
{% endfor %}
The error is:
Cannot resolve keyword 'dwarf_set' into field. Choices are: id, job, name
SOLUTION
view:
def fortress(request):
careers = Career.objects.all().annotate(dwarfs_in_career = Count('job__dwarf'))
return render_to_response('ragna_base/fortress.html', {'careers': careers})
template:
{% for career in careers reversed %}
<li>{{ career.name }}: {{ career.dwarves_in_career }}</li>
{% endfor %}
EVEN BETTER SOLUTION
careers = Career.objects.filter(Q(job__dwarf__user = 1) | Q(job__dwarf__user__isnull = True)) \
.annotate(dwarves_in_career = Count('job__dwarf'))
Don't forget to from django.db.models import Count, Q
What I like about the above solution was it not only returns careers that have dwarves working but even the careers that have none which was the next problem I encountered. Here's my view for completeness:
<ul>
{% for career in careers %}
<li>{{ career.name }}: {{ career.dwarves_in_career }}</li>
{% endfor %}
</ul>
Django's ORM isn't gonna make this uber-simple. The simple way is to do something like:
for career in Career.objects.all():
career.dwarf_set.all().count()
That will execute 1 query for each job (O(n) complexity).
You could try to speed that up by using Django's Aggregation feature, but I'm not entirely sure if it'll do what you need. You'd have to take a look.
The third option is to use custom SQL, which will absolutely get the job done. You just have to write it, and maintain it as your app grows and changes...
Does this do what you want?
from django.db.models import Count
Career.objects.annotate(Count('dwarf'))
Now each career object should have a dwarf__count property.
Can't you just get a count grouped by career? And do an outer join if you need the zero rows returned too.