Django DetailView with few url parameters - django

Let's say I have next models:
class Category(models.Model):
cat_name = models.CharField(u'name',max_length=50, unique=True)
slug = models.SlugField(u'URL',unique=True)
class News(models.Model):
title = models.CharField(max_length=250)
slug = models.SlugField(unique=True)
category = models.ForeignKey(Category,null=True,on_delete=models.SET_NULL)
And my get_absolute_url function looks like that:
def get_absolute_url(self):
return ('news:detail',(), {'slug':self.slug})
And url looks like /article/<slug>
If I want to have URL like <category.slug><news.slug> ? How can I do that?

You can do that:
# models.py
def get_absolute_url(self):
return ('news:detail', (), {
'category_slug': self.category.slug,
'slug': self.slug,
})
# views.py
class NewsDetail(DetailView):
model = News
slug_field = 'slug'
slug_url_kwarg = 'slug'
def get_queryset(self):
return News.objects.filter(category__slug=self.kwargs['category_slug'])
# urls.py
url(r'(?P<category_slug>[a-z0-9\-]+)/(?P<slug>[a-z0-9\-]+)/$', NewsDetail.as_view(), name='news')
So the idea is to add 2 parameters to your url definition. Last one is usual to query a news row and the first one will used by get_queryset to filter the news by the specified category.
More about parameters in urls in docs - https://docs.djangoproject.com/en/1.9/topics/http/urls/#named-groups

Related

How to display a template based on a queryset done in a class based view where one of the argument for queryset comes from URL parameter

How to generate a template of all 'posts' that a 'user' wrote.
Idea: Client click on the 'user' link from the main site and we are taken to the page with all 'posts' user have generated.
I have tried so many things in views.py, part of them worked so only last not working solution will be attached. :)
I try to get a 'user' parameter from the URL so for example:
blogname/user -> that URL will generate list of all 'user' posts.
urls.py
urlpatterns = [
path('author/<str:username>', AuthorPostIndexView.as_view(),
name='author_post_index'),
]
models.py
class Person(models.Model):
username = models.CharField(max_length = 50, null=True, unique = True)
post = models.ManyToManyField(Post,blank=True,null=True, related_name='authors')
class Post(models.Model):
title = models.CharField(max_length=255, blank=True, null=True)
views.py
# - commented out as non of these worked. Some of them I have broken
playing around with desperation to fix so they are with lack logic,
sketches. Tried those one by one.
class AuthorPostIndexView(ListView):
model = Person
template_name ='authorpostindex.html'
# def get_queryset(self):
# username = self.kwargs['username']
# authorpost = username.post.all()
# return username
# def get_queryset(self):
# if self.request.method == 'GET':
# queryset = Person.objects.all()
# url_username = self.kwargs('username', None)
# if url_username is not None:
# queryset = queryset.filter(person__username=url_username).post.all()
# else:
# queryset = "No queryset"
# def get(self, request, *args, **kwargs):
# author = Person.objects.get(username=username)
# authorpost = author.post.all()
# return authorpost
authorpostindex.html
# Tried different solution depending from queryset, none worked.
Thanks
ps. Solution:
class AuthorPostIndexView(ListView):
model = Person
template_name ='authorpostindex.html'
context_object_name = 'author_post'
def get_queryset(self):
username = self.kwargs['username']
queryset = Person.objects.get(username=username).post.all()
return queryset
pss. Better one below.
Your view should be based on Post. Then you can use all the existing logic of the view but additionally filter by the author username.
class AuthorPostIndexView(ListView):
model = Post
template_name ='authorpostindex.html'
def get_queryset(self):
queryset = super().get_queryset()
username = self.kwargs['username']
return queryset.filter(authors__username=username)
(Note, it's a bit odd to store the username as a CharField on Person only. You should probably have a ForeignKey to the User model.)

Django Using Slug Field for Detail URL

I'm attempting to setup my site so that the url for my job-detail will use a slug field instead of a pk. It's telling me that it cannot find my job with the given slug (which is an int, 147).
Update:
After looking at the DetailView description at https://ccbv.co.uk/projects/Django/1.11/django.views.generic.detail/DetailView/ I realized there is a slug_field attribute for DetailView. My new view looks like:
class JobDetailView(CacheMixin, DetailView):
model = Job
slug_field = 'slug'
Question:
urls:
urlpatterns = [
url(r'^careers$', views.job_list, name='job-list'),
url(r'^careers/(?P<slug>[0-9]+)/$', views.JobDetailView.as_view(), name='job-detail'),
]
view:
class JobDetailView(CacheMixin, DetailView):
model = Job
pk_url_kwarg = 'slug'
def get_object(self, *args, **kwargs):
# Call the superclass
object = super(JobDetailView, self).get_object()
# Return the object
return object
def get(self, request, *args, **kwargs):
object = super(JobDetailView, self).get(request, *args, **kwargs)
return object
model:
class Job(UpdateAble, PublishAble, models.Model):
slug = models.CharField(unique=True, max_length=25)
facility = models.ForeignKey('Facility')
recruiter = models.ForeignKey('Recruiter')
title = models.TextField()
practice_description = models.TextField(blank=True, default="")
public_description = models.TextField(blank=True, default="")
objects = JobManager()
def get_next(self, **kwargs):
jobs = Job.objects.published()
next = next_in_order(self, qs=jobs)
if not next:
next = jobs[0]
return next
def get_prev(self, **kwargs):
jobs = Job.objects.published()
prev = prev_in_order(self, qs=jobs)
if not prev:
prev = jobs[len(jobs)-1]
return prev
def __str__(self):
return f'{self.facility}; {self.title}'
manager:
class JobManager(models.Manager):
def published(self):
return super(JobManager, self).get_queryset().filter(is_published=True).order_by('facility__name', 'title')
You actually don't need to define pk_url_kwarg at all, and in fact by doing so you have confused things leading to the object not being found.
As you can see from the default implementation of get_object, the view normally looks for either a pk or slug kwarg in the URL; whichever it finds will be used for the lookup. But by setting pk_url_kwarg to slug, you're telling the view to get the URL kwarg named "slug" but use it to look up against the PK field, which obviously won't work.
Just remove that attribute altogether, and Django will detect your slug kwarg and use it to correctly look up against the slug field.

Foreign key lookups - slug URL with Django generic list view

I have been searching here and reading the documentation and experimenting in python, but I can't find a solution to my particular mess. I did the Django tutorial, but I'm still confused as to how to pass stuff thru the URL in django when starting to use foreign keys, I'm sure it's pretty simple. I'm a new django user and this is my first post. I have the models Playlist, and PlaylistEntry with mixed relationships to user and videos (not posted). I'm trying to show a detail view that uses a slug of a playlist title, to pull out entries in the playlist. Eg. In python, I can do
entries = PlaylistEntry.objects.filter(playlist__slug='django')
which returns all my entries in the playlist 'Django' correctly.
Here are my models...
class Playlist(models.Model):
class Meta:
verbose_name_plural = 'playliztz'
title = models.CharField(blank=True,
help_text=u'Title of playlist',
max_length=64,
)
user = models.ForeignKey('userprofile.UserProfile',
blank=False,
help_text=u'owns this playlist',
null=False, )
slug = models.SlugField(u'slug',
max_length=160,
blank=True,
editable=False
)
created = models.DateTimeField(editable=False)
modified = models.DateTimeField(editable=False)
def __unicode__(self):
return self.title
def get_absolute_url(self):
return reverse('playlist-detail', kwargs={'pk': self.pk})
def save(self):
self.slug = slugify(self.title)
if not self.id:
self.created = datetime.datetime.today()
self.modified = datetime.datetime.today()
super(Playlist,self).save()
class PlaylistEntry(models.Model):
class Meta:
verbose_name_plural = "playlist entries"
video = models.ForeignKey('video.Video',
blank=True,
default=None,
help_text=u'the video title',
null=True, )
playlist = models.ForeignKey('Playlist',
blank=True,
default=None,
help_text=u'Belongs to this playlist',
null=True,)
def __unicode__(self):
return self.video.video_title
My URLS looks like this...
url(r'^playlist/$', PlaylistList.as_view(), name='user_playlists'),
url(r'^playlist/(?P<slug>[0-9A-Za-z-_.//]+)/$', PlaylistDetail.as_view(), name='playlist_entries'),
And my Views.py looks like this...
class PlaylistList(LoggedInMixin, ListView): # shows a list of playlists
template_name = 'userprofile/playlist_list.html'
model = Playlist
context_object_name = 'playlist_list'
def get_queryset(self):
"""Return currently users playlists."""
return Playlist.objects.filter(user__user=self.request.user)
def get_context_data(self, **kwargs):
context = super(PlaylistList, self).get_context_data(**kwargs)
if not self.get_queryset():
context['error'] = "You don't have any playlists yet."
return context
else:
return context
class PlaylistDetail(LoggedInMixin, DetailView):
model = PlaylistEntry
template_name = 'userprofile/playlist_detail.html'
def get_queryset(self):
self.current_playlist = get_object_or_404(Playlist, slug=self.kwargs['slug'])
# CHECK - Prints out correct entries for playlist in slugfield (current_playlist)
self.entries = PlaylistEntry.objects.filter(playlist__title=self.current_playlist.title)
print self.entries
# Should expect to return the same queryset?
return PlaylistEntry.objects.filter(playlist__title=self.current_playlist.title)
def get_context_data(self, **kwargs):
context = super(PlaylistEntry, self).get_context_data(**kwargs)
context['entries'] = PlaylistEntry.objects.all()
return context
self.entries prints the correct entries for this playlist in the Check bit.
In my playlist template I am using a link sending the playlist.slug - the url looks correct like this /user/playlist/this-particular-playlist-slug.
the error is...
Cannot resolve keyword u'slug' into field. Choices are: id, playlist, video
You've made things much more complicated than they need to be.
The model for your detail view should still be Playlist, not PlaylistEntry. The reason you're getting that error is that the slug is on the Playlist model, but you've told the view to filter on PlaylistEntry.
What you actually want to to is to pass the single Playlist identified by the slug into the template. From there, you can easily iterate through the detail objects associated with that playlist via the reverse relation.
So, change that model setting, and drop both get_context_data and get_queryset from the detail view: you don't need them. Then in the template you can simply do:
{% for entry in playlist.playlistentry_set.all %}

Django Slug Not Working In get_absolute_url

Whats wrong with this code? get_absolute_url is blank when rendered in my template, which means It's failing somewhere.
I suspect it's the slug as this is the first time I have tried to use it within Django:
Thanks
Model:
class Entry(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=255, unique=True)
def get_absolute_url(self):
return reverse("EntryDetail", kwargs={"slug": self.slug})
URL:
url(r'^entry/(?P<slug>[^\.]+).html',
blog_views.EntryDetail.as_view(),
name='blog_entry'),
View:
class EntryDetail(DetailView):
context_object_name = 'entry'
template_name = "blog.entry.html"
slug_field = 'slug'
def get_object(self):
return get_object_or_404(Entry, url=self.slug_field)
Based on what #Peter DeGlopper stated in comments: It looks like your trying to get the absolute URL using a Class Based View. Don't use reverse here, instead you should always give your URLs a name, and then refer to that.
For example this is how it should look:
model
#models.permalink
def get_absolute_url(self):
return 'blog_entry', (), {'slug': self.slug}
urls
url(r'^entry/(?P<slug>[^\.]+).html',
blog_views.EntryDetail.as_view(),
name='blog_entry'),

How do I write a write a view in django that gets an object_list based on a field?

I have a simple tag model and a simple project model.
In the project model I have a m2m to the tag model.
I want to return all the projects with a tag. I'm almost there.
Right now the view below returns invalid literal for int() with base 10: 'cheap'
So, it has the right slug, and it's making the query, but it's trying to get the list of projects based on the id of the m2m tag.
Any suggestion much appreciated.
My Tag Model:
class Tag(models.Model):
"""
A basic tag model for projects
"""
name = models.CharField(max_length=100, unique=True)
slug = models.CharField(max_length=100)
description = models.TextField(blank=True)
class Meta:
ordering = ('name',)
verbose_name = _('Tag')
verbose_name_plural = _('Tags')
def __unicode__(self):
return self.name
#models.permalink
def get_url_path(self):
return ('TagDetail', (), {'slug': self.slug})
My url:
# tags/<slug>/ The detail view for an archived project
url(regex=r'^tags/(?P<slug>[\w-]+)/$',
view=TagDetail.as_view(),
name='tag_detail',
),
My view I'm trying to figure out:
class TagDetail(ListView):
""" Get all projects for a tag """
template_name = "projects/TagDetail.html"
def get_queryset(self):
tags = get_list_or_404(Project, tags=self.kwargs['slug'], displayed=True)
paginate_by = 10
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(TagDetail, self).dispatch(*args, **kwargs)
Assuming your Project model looks like this
class Project( models.Model ):
tags=models.ManyToManyField( Tag )
match to the tag's slug
def get_queryset( self ):
return get_list_or_404(Project, tags__slug=self.kwargs['slug'], displayed=True)
the only change being tags__slug.