I have used generic views.Every Episode is connected to a certain season through ForeignKey. In views.py I have these:
class SeasonList(generic.ListView):
template_name = 'episodes/episodes.html'
context_object_name = 'all_seasons'
def get_queryset(self):
return reversed(Season.objects.all())
# Here I need to sort the episodes
class SeasonDetails(generic.DetailView):
model = Season
template_name = 'episodes/season_details.html'
In the list view I used reversed() to show the latest season first. Similarly, In the detail view, I want the episodes to appear in the descending order because the latest episode should appear at the top of the page.
In my html I have accessed the episodes list using season.episode_set.all
{% for episode in season.episode_set.all %}
<!-- the tags to show the list -->
{% endfor %}
Is there any way how I can reverse the episode list?
You could order by id and use descendant - or ascendant depending on your need
Season.objects.all().order("id") # Ascendant
Season.objects.all().order("-id") # Decendant
Or reverse() will be ok to reverse the queryset whatever your filter.
Season.objects.all().reverse()
To sort the episodes, you can use something like this -
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['episodes'] = Episodes.objects.all().order_by('-date_posted')
return context
Related
I'm working on a small e-commerce. For a model 'Product' I keep track of the stock using a Custom Manager and a method called Products.objects.with_stock() that uses annotations (I'm required to do so, there's no way I can add a stock attribute in Product)
In a ListView of all the products, it's pretty straightforward to add the stock, to access it from a template:
# app/views.py
class ProductListView(ListView):
...
def get_stock(self, obj):
return obj.stock
def get_queryset(self):
return super().get_queryset().with_stock()
And then in the template I just call it:
<!-- templates/home.html-->
{% for product in products %}
<p> Stock: {{product.stock}} <p>
{% endfor %}
How do I perform something similar for a DetailView?
Given that a DetailView gets a particular object instance, where or how do I run something similar to what I did in the ListView? Where do I run that query method that affects all objects as a whole, so that then I can access it in the same way from the template?
It couldn't be simpler: just do the same in the DetailView:
# app/views.py
class ProductDetailView(DetailView):
...
def get_stock(self, obj):
return obj.stock
def get_queryset(self):
return super().get_queryset().with_stock()
Django pagination in TemplateView?
Hello, I am trying to figure out how I can access multiple models in a Class View and paginate at the same time.
My Outcome after reading, DjangoDoc and Stackoverflow:
ListView - I simply can use paginate_by= but I can load only one Model.
TemplateView - I can load many models but can not use paginate_by=?
For example three Models: Chicken, Cows and Cats (and I want to display on my page the last 3 entries of each model). All Models have a model field called entry date.
class HomeIndex(TemplateView):
template_name = 'home.html'
paginate_by = 3 # --- something like that
def get_context_data(self, **kwargs):
context = super(HomeIndex, self).get_context_data(**kwargs)
context['chickens'] = Chicken.objects.order_by('-entry_date'')
context['cows'] = Cows.objects.order_by('-entry_date'')
context['cats'] = Cats.objects.order_by('-entry_date')
return context
Or maybe I can add something to objects.order_by('-entry_date', < pagination? >).
Thanks
Django QuerySet has built-in results slicing.
Cows.objects.order_by('-entry_date'')[offset:limit]
For the last 3 entries, offset is 0 and the limit 3
Cows.objects.order_by('-entry_date'')[0:3]
or the same can be written in a more pythonic way
Cows.objects.order_by('-entry_date'')[:3]
To get last 3 cows, cats and chicken, following code will work.
def get_context_data(self, **kwargs):
context = super(HomeIndex, self).get_context_data(**kwargs)
context['chickens'] = Chicken.objects.order_by('-entry_date')[:3]
context['cows'] = Cows.objects.order_by('-entry_date')[:3]
context['cats'] = Cats.objects.order_by('-entry_date')[:3]
return context
References:
Limiting Queries link
If you want to use ListView you still can, by chaining the querysets in get_queryset(self) and paginating that (read this answer to see the chain and sorted explained). This way you can use the default simple pagination.
from itertools import chain
from operator import attrgetter
class HomeIndex(ListView):
template_name = 'home.html'
paginate_by = 3 # --- somehting like that
def get_queryset(self):
chicken_list = Chicken.objects.all()
cow_list = Cows.objects.all()
cat_list = Cats.objects.all()
result_list = sorted(
chain(chicken_list, cow_list, cat_list),
key=attrgetter('entry_date'),
reverse=True)
return result_list
Then in your template:
{% for data in object_list %}
{{ data }}
{% endfor %}
And you can use the pagination as shown here.
My Django project has a field for "Tags," where users can add tags for their posts. Currently this is stored in a models.CharField(max_length=200) field in my model.
What I'd like to do is have it so that when the post is displayed, each word is printed with its own URL to sort the posts by that particular tag. For example, each post also has a "Category" models.CharField where they can select a category from a dropdown list, which is made into a URL in the post.
For example in my Views.py:
#login_required
def category(request, category):
thisuser = request.user
if request.method == "POST":
category = request.POST['category']
else:
category = ''
following = Follow.objects.filter(follower=thisuser).order_by('-pubdate')
followers = Follow.objects.filter(who_following=thisuser).order_by('-pubdate')
posts = Post.objects.filter(category__contains=category)
args = {'posts': posts, 'thisuser': thisuser, 'following': following, 'followers': followers}
args.update(csrf(request))
args['category'] = category
return render_to_response('lobby.html', args)
and in my template:
Category: {{post.category}}
Is there a {% for %} template tag I can use to split the values in the tags field by comma and then render each of them as their own instance? Or would I have to do something else, like make a relational database for Tags (since a post can have multiple tags) and then iterate them all by Post ID?
Database design-wise, the best option is to have a separate model for Tags. This will let you search/sort/filter by tags more easily.
However, if you need a quick fix, you'll have to push that "split by comma" logic to your view/model, like so:
class Post(models.Model):
tags = models.CharField(...)
def split_tags(self):
return self.tags.split(',')
# in your template:
{% for tag in post.split_tags %} {{ tag }} {% endfor %}
You should do something like
class Post(models.Model):
def categories(self):
return self.category.split(',')
and then
{% for category in post.categories %}
but I strongly advice using a m2m relationship in your case. You can use an app like django-taggit to help you with that.
As a Django beginner I'm working on the the tutorial provided by django docs at https://docs.djangoproject.com/en/1.5/intro/tutorial04/
In it they demonstrate a list of multiple polls that are listed using a query by publication date. Could I add another list to be also used in the template to be used as well. Example Displaying a list of latest polls by date and another by alphabetical order on the same page.
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_poll_list'
def get_queryset(self):
"""Return the last five published polls."""
return Poll.objects.order_by('-pub_date')[:5]
Absolutely, you'll just need to write your own 'get_context_data' method that will retrieve those values and then they will be available in the view. Something like:
def get_context_data(self, *args, **kwargs):
context = super(IndexView, self).get_context_data(*args, **kwargs)
context['alphabetical_poll_list'] = Poll.objects.order_by('name')[:5]
return context
With this both {{ latest_poll_list }} and {{ alphabetical_poll_list }} would be available in your template.
I have a like model that collects likes that users select on books.
So, each record has the user_id, like_id and book_id.
I want a url that is something like:
(?P<top_num>\d+)/likes/
Wich would be directed to a view that does something like this:
class TopLikes(ListView):
""" Get all the archived projects """
queryset = Like.objects.filter(display=True)
template_name = "books/TopLikes.html"
paginate_by = 10
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(TopLikes, self).dispatch(*args, **kwargs)
What I don't know is how to take that top_num as the number to pass to the view to return the top ten books with the most likes.
it would make sense to me to do something like queryset = Like.objects.filter(display=True).annotate(num_books=Count('books')).order_by('num_books')
It makes sense to me to get the likes and then use the likes to do something like this in the template:
{% for object in object_list %}
{{ object.book.title }} with {{ object|length }}
{% endfor %}
Would this just be easier to do as a custom view?
Thanks
Override get_queryset() method, so that you can add custom filtering
Use self.kwargs, so that you can use top_num url parameter to limit your queryset
Use {{ object.num_books }}, because well what is {{ object|length }} supposed to do anyway :)
Example:
class TopLikes(ListView):
""" Get all the archived projects """
queryset = Like.objects.filter(display=True)
template_name = "books/TopLikes.html"
paginate_by = 10
def get_queryset(self):
qs = super(TopLikes, self).get_queryset()
qs = qs.annotate(num_books=Count('books')).order_by('num_books')
qs = qs[:self.kwargs['top_num']]
return qs