View down below should show the tasks for the logged user but when I paginate the tasks I got
Cannot filter a query once a slice has been taken.
how should I filter the context to avoid the error for pagination?
class TaskList(LoginRequiredMixin, ListView):
model = Task
context_object_name = 'tasks'
template_name = 'TaskList.html'
paginate_by = 2
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['tasks'] = context['tasks'].filter(user=self.request.user)
return context
Use get_queryset() for filtering objects in ListView:
def get_queryset(self): # noqa: D102
queryset = super().get_queryset()
return queryset.filter(user=self.request.user)
The filtering in get_queryset() method should fix the issue - also remove get_context_data() method overload, it's not neccessary.
Related
How can I get pagination for the current code? I can't change the DetailView to View or ListView, so in this class I get a filter of products.
class CategoryDetailView(DetailView):
model = Category
queryset = Category.objects.all()
context_object_name = 'category'
template_name = 'category_products.html'
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
...
products = Product.objects.filter(id__in=[pf_['product_id'] for pf_ in pf])
context['category_products'] = products
return context
Typically one picks the class-based view that is the most convenient to do the job. You can see your view as a ListView of Products that are related to a certain category. By doing this, you make it more convenient to retrieve the related Products, and less convenient to obtain the relevant Category, but the last one is easier.
We can implement the view as:
from django.shortcuts import get_object_or_404
class CategoryDetailView(ListView):
model = Product
context_object_name = 'category_products'
template_name = 'category_products.html'
paginate_by = 1
# …
def get_queryset(self):
qs = super().get_queryset().filter(
category__slug=self.kwargs['slug']
)
qd = self.request.GET.copy()
qd.pop(self.page_kwarg, None)
if 'search' in self.request.GET:
qs = qs.filter(title__icontains=self.request.GET['search'])
elif qd:
qs = qs.filter(
features__value__in=[v for _, vs in qd.lists() for v in vs]
)
if 'sort' in self.request.GET:
qs = qs.order_by(self.request.GET['sort'])
return qs
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['category'] = get_object_or_404(Category, pk=self.kwargs['pk'], slug=self.kwargs['slug'])
return context
The get_queryset here thus implements the advanced querying you implemented, and we add the category by overriding the get_queryset. This queryset will then automatically get paginated by the logic of the ListView.
I would however advise to simplify the logic, usually advanced filtering only makes it more error prone. Furthermore ordering by an arbitrary parameter introduces a security vulnerability, one could for example try to filter on some_foreignkey__some_secure_field to expose data. You thus might want to validate that the order is in a list of acceptable values.
I want to add pagination to my List View. I have used paginated_by=10 but it not working. Please help me to add pagination to my view in my template. What HTML should i put in my template
View.py
class CompanyListView(LoginRequiredMixin, generic.TemplateView):
template_name = 'superadmin/company/company.html'
def get_context_data(self, **kwargs):
context = super(CompanyListView, self).get_context_data(**kwargs)
context['companies'] = Company.objects.exclude(company_name='Apollo').exclude(company_is_deleted = True).annotate(number_of_company_users=Count('userprofile'))
return context
You could use ListView instead of TemplateView.
Here is how.
class CompanyListView(LoginRequiredMixin, generic.ListView):
template_name = 'superadmin/company/company.html'
queryset = Company.objects.all()
context_object_name = 'companies'
paginate_by = 10
def get_queryset(self):
return (
self.queryset.exclude(company_name='Apollo')
.exclude(company_is_deleted =True)
.annotate(number_of_company_users=Count('userprofile'))
)
I'm trying to filter a model with get_queryset() and it seems to work in the view but not in the template.
My view :
class FolderCreate(CreateView):
fields = ['name', 'parent']
template_name = 'Form/folder_create.html'
def get_queryset(self):
folders = Folder.objects.filter(owner=self.request.user)
print folders # ==> return [<Folder: Folder>, <Folder: Another folder>]
return folders
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.owner = self.request.user
return super(FolderCreate, self).form_valid(form)
def get_initial(self):
if self.request.method == 'GET':
foldersUrl = self.request.META['HTTP_REFERER'].split('/')
foldersUrl.pop()
folder = urllib2.unquote(foldersUrl[-1])
try:
return {'parent' : Folder.objects.get(name=folder, owner=self.request.user)}
except Folder.DoesNotExist:
pass
As you can see, folders return two objects related to the session user in get_queryset() : 'Folder' and 'Another folder
Infortunately, the combobox of my template get all the folders, without any filtering.
Any idea ?
The issue here is that get_queryset is not used in a CreateView, as it's meant for filtering the models returned for display in a list or detail view. You want something completely different: you want to filter the choices available in a form field.
To do that you will need to create a custom ModelForm that accepts a user kwarg and filters the queryset accordingly:
class FolderForm(forms.ModelForm):
class Meta:
model = Folder
fields = ['name', 'parent']
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(FolderForm, self).__init__(*args, **kwargs)
self.fields['parent'].queryset = Folder.objects.filter(user=user)
and then change your view to use that form and pass in the user parameter:
class FolderCreate(CreateView):
template_name = 'Form/folder_create.html'
form_class = FolderForm
def get_form_kwargs(self):
kwargs = super(FolderCreate, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
I'm using Django endles-pagination to load the pages in infinite scroll. I also have some filters that filter the data according to the criteria (for eg, price slider filtering according to price). Now when the page loads, the filter right now filters only from the page loaded, though I want it to filter it from all the pages that have been or are to be loaded. Is there a way to do this (by making some ajax request or something)?
Any help on this would be great. Thanks a lot.
To filter the data you have to redefine get_queryset() method in the views requesting the filtered query.
For example I request the current language in template to filter the Blog posts based on the language:
class Blog(AjaxListView):
context_object_name = "posts"
template_name = 'cgapp/blog.html'
page_template = 'cgapp/post_list.html'
def get_queryset(self):
if self.request.LANGUAGE_CODE == 'en': #request value of the current language
return News.objects.filter(language='en') #return filtered object if the current language is English
else:
return News.objects.filter(language='uk')
To filter the queryset based on the users input, you may refer to POST method:
from app.forms import BlogFilterForm
class Blog(LoginRequiredMixin, AjaxListView):
context_object_name = "posts"
template_name = 'blog/blog.html'
page_template = 'blog/post_list.html'
success_url = '/blog'
def get_queryset(self): # define queryset
queryset = Post.objects.all() # default queryset
if self.request.method == 'POST': # check if the request method is POST
form = BlogFilterForm(self.request.POST) # define form
if form.is_valid():
name = form.cleaned_data['name'] # retrieve data from the form
if name:
queryset = queryset.filter(name=name) # filter queryset
else:
queryset = queryset
return queryset
def get_context_data(self, **kwargs):
context = super(Blog, self).get_context_data(**kwargs)
context['form'] = BlogFilterForm() # define context to render the form on GET method
return context
def post(self, request, *args, **kwargs): # define post method
return super(Blog, self).get(request, args, kwargs)
The endless pagination should work fine.
I'm using Django v1.4 and I'm trying to subclass the generic ListView view. Here's the code
from django.views.generic import ListView
class SearchListView(ListView):
model = None
fields = None
def get_queryset(self):
#...etc...
return super(SearchListView, self).get_queryset()
Then I'll further customize that view for a particular model:
class PersonSearchListView(SearchListView):
model = Person
fields = ['first_name', 'last_name']
So what happens is, ImproperlyConfigured exceptions are the superclass (ListView) stating that either model or queryset should be defined. I thought I was... (model = Person). Why is this value not making it into the view?
Thanks
When you call super(SearchListView, self).get_queryset()
You will call the get_queryset of the below class, as you can see it will raise an exception if you didn't set the model or queryset.
ListView is a child of MultipleObjectMixin.
But if you instantiate a PersonSearchListView, the model should have been set correctly. Could you include the url config? Will try it out later and update my answer.
class MultipleObjectMixin(ContextMixin):
"""
A mixin for views manipulating multiple objects.
"""
allow_empty = True
queryset = None
model = None
paginate_by = None
context_object_name = None
paginator_class = Paginator
def get_queryset(self):
"""
Get the list of items for this view. This must be an iterable, and may
be a queryset (in which qs-specific behavior will be enabled).
"""
if self.queryset is not None:
queryset = self.queryset
if hasattr(queryset, '_clone'):
queryset = queryset._clone()
elif self.model is not None:
queryset = self.model._default_manager.all()
else:
raise ImproperlyConfigured("'%s' must define 'queryset' or 'model'"
% self.__class__.__name__)
return queryset