Trying to filter image by category in Django
I am trying to filter an image through a category. It works but not the way I want it to. Here are how my models are setup:
class Image(models.Model):
CATEGORY_CHOICES = (
('Cover', 'Cover'),
('Scan', 'Scan'),
('Other', 'Other'),
)
title = models.CharField(max_length=256)
image = models.ImageField(upload_to="images/")
category = models.CharField(max_length=10, choices=CATEGORY_CHOICES)
contributor = models.ManyToManyField(Contributor, blank=True, null=True)
date_added = models.DateField(auto_now_add=True)
def __unicode__(self):
return self.title
class Meta:
ordering = ['title']
class Issue(models.Model):
...
images = models.ManyToManyField(Image, related_name="images_inc", blank=True, null=True)
....
def __unicode__(self):
return u'%s #%s' % (self.title, self.number)
def get_absolute_url(self):
return "/issues/%s" % self.slug
class Meta:
ordering = ['-date_added']
Views.py:
def issue(request, issue_slug):
issue = get_object_or_404(Issue, slug=issue_slug)
cover = Image.objects.filter(category='Cover')
scan = Image.objects.filter(category='Scan')
return render_to_response('comics/issue_detail.html', {'issue': issue, 'cover': cover, 'scan': scan}, context_instance=RequestContext(request))
I am trying to filter it by Cover or Scan, as you can see, but when I put this into my template:
{{ cover }} or {{ scan }}
It returns:
<Image: Astonishing X-Men 1 Cover A>] [<Image: Astonishing X-Men 1, teamshot>]
I need it to return the image URL, obviously. Adding {{ cover.url }} doesn't work.
Oh, and I just realized it does not display the specific image that is in the issue. It displays ALL images that categorized as either Scan 开发者_运维百科or Cover.
In your current code, you're returning all the images, even the ones that aren't associated with your chosen comic issue. I'm assuming you want the ones for the selected issue. You probably want to do this instead:
def issue(request, issue_slug):
issue = get_object_or_404(Issue, slug=issue_slug)
try:
cover = issue.images_inc.filter(category='Cover')[0]
except IndexError:
cover = None
try:
scan = issue.images_inc.filter(category='Scan')[0]
except IndexError:
scan = None
return render_to_response('comics/issue_detail.html', {'issue': issue, 'cover': cover, 'scan': scan}, context_instance=RequestContext(request))
Then, in the template:
{% if cover.image %}
<img src="{{ cover.image.url }}" alt="{{ cover.image.title }}" />
{% endif %}
{% if scan.image %}
<img src="{{ scan.image.url }}" alt="{{ scan.image.title }}" />
{% endif %}
Update
If you do want more than one cover or scan, then you don't want to get only the first item. And really, you should probably just move this logic to models.py
to avoid possible code duplication.
Something like this would be better:
# models.py
class Issue(models.Model):
...
def scans(self):
return self.images.filter(category='Scan')
def covers(self):
return self.images.filter(category='Cover')
# views.py
def issue(request, issue_slug):
issue = get_object_or_404(Issue, slug=issue_slug)
return render_to_response('comics/issue_detail.html', {'issue': issue }, context_instance=RequestContext(request))
Then, in the template:
<ul>
{% for cover in issue.covers %}
<li><img src="{{ cover.image.url }}" alt="{{ cover.image.title }}" /></li>
{% empty %}
<li>No cover</li>
{% endfor %}
</ul>
<ul>
{% for scan in issue.scans %}
<li><img src="{{ scan.image.url }}" alt="{{ scan.image.title }}" /></li>
{% empty %}
<li>No scans</li>
{% endfor %}
</ul>
filter always returns the list of matches in an array.
If you just want the first match, you could do something like this
cover = issue.images.filter(category='Cover')[:1][0]
scan = issue.images.filter(category='Scan')[:1][0]
But that will throw an exception if there is no match, so maybe better to wrap in in a try except such as
try:
cover = issue.images.filter(category='Cover')[:1][0]
except:
cover = None
In the template, since cover and scan represent image objects, you can get the url with this syntax:
{{ cover.image.url }} or {{ scan.image.url }}
The alternative if you want to see all of the images is to load covers and scans (plural) and check to see if they are non empty in your template. There is no need to except for the index error here, and it is much simpler.
In this case you would do:
covers = issue.images.filter((category='Cover'))
And then in the template:
{% if covers %}
{% for cover in covers %}
{{cover.image.url}} - {{cover.title}}
{% endfor %}
{% endif %}
精彩评论