Reputation: 123
I'm trying to include an template and utilize it in two different views, where the first one gets one url param and the second gets the same plus another one. Inside my included template there is an iteration with a {% url %} tag that I need to pass both params, since the second view needs them, but doing this causes NoReverseMatch when trying to render my first view, probably because it only accepts one param. Is there any way to specifying the second param is optional?
Here is my code:
...
url(r'^portfolio/(?P<category_slug>[-\w]+)/$', views.category, name='category'),
url(r'^portfolio/(?P<category_slug>[-\w]+)/(?P<album_slug>[-\w]+)/$', views.album, name='album'),
...
...
class Album(models.Model):
cover = models.ImageField()
title = models.CharField(max_length=200, unique=True)
description = models.CharField(max_length=200, blank=True)
posts = models.ManyToManyField(Post, blank=True)
slug = models.SlugField(max_length=200)
class Category(models.Model):
cover = models.ImageField()
title = models.CharField(max_length=200, unique=True)
albums = models.ManyToManyField(Album, blank=True)
slug = models.SlugField(max_length=200)
@receiver(pre_save, sender=Album)
@receiver(pre_save, sender=Category)
def save_slug(sender, instance, *args, **kwargs):
instance.slug = slugify(instance.title)
...
...
def index(request):
main_categories = Category.objects.filter(...)
return render(request, 'index.html', {'main_categories': main_categories})
def category(request, category_slug):
try:
category_selected = Category.objects.get(slug=category_slug)
except Category.DoesNotExist:
category_selected = None
albums = category_selected.albums.all()
return render(request, 'category.html', {
'category_selected': category_selected,
'albums': albums
})
def album(request, category_slug, album_slug):
try:
category_selected = Category.objects.get(slug=category_slug)
album_selected = Album.objects.get(slug=album_slug)
except Category.DoesNotExist:
category_selected = None
except Album.DoesNotExist:
album_selected = None
posts = album_selected.posts.all()
return render(request, 'album.html', {
'category_selected': category_selected,
'album_selected': album_selected,
'posts': posts
})
...
...
{% for obj in objects %}
...
<a href="{% url view category_slug=obj.slug album_slug=obj.slug %}">
...
{% endfor %}
...
...
{% include 'includedtemplate.html' with objects=main_categories view='category' %}
...
EDIT: I've managed to solve this by separating my urls with only one different slug each. This is simpler and fits my situation better, considering that I had a M2M for Category and Album and this could cause many urls for a single album.
Upvotes: 0
Views: 943
Reputation: 5067
You can combine views and set None for album_slug.
def combined_view(request, category_slug, album_slug=None):
category_selected = Category.objects.filter(slug=category_slug).first()
album_selected = None
posts = None
template = 'category.html'
if album_slug:
album_selected = Album.objects.filter(slug=album_slug).first()
posts = album_selected.posts.all()
template = 'album.html'
return render(request, template, {
'category_selected': category_selected,
'album_selected': album_selected,
'posts': posts
})
Also the order of urls is important - first url should be with one parameter, second with two parameters:
url(r'^portfolio/(?P<category_slug>[-\w]+)/$', views.combined_view, name='cview'),
url(r'^portfolio/(?P<category_slug>[-\w]+)/(?P<album_slug>[-\w]+)/$', views.combined_view, name='cview'),
P.S. Instead of try..except in views you can use filter().first(). It is faster to write.
Upvotes: 0