I've filtering view of django_filters.FilterSet which is called right from urls.py
url(r'^$', FilterView.as_view(filterset_class=ProductFilter, template_name='products/products.html'), name='products'),
and it's has no pagination, but when i add paginate_by = 20 in
url(r'^$', FilterView.as_view(filterset_class=ProductFilter, template_name='products/products.html'), paginate_by = 20, name='products'),
it adds my custom pagination page, but it's not handling data restricted by filters. So i can apply a few filters and it reduces data to, say 40 rows, but clicking on a second page it loads my all data without any filter. Could I specify that I want to paginate data after filtering somehow?
At the end I decided to create separate view and add queryset directly to context object like:
class ProductView(ListView):
model = Product
template_name = 'products/products.html'
paginate_by = 5
def get_context_data(self, **kwargs):
context = super().get_context_data()
context['product_list'] = ProductFilter(self.request.GET, queryset=Product.objects.order_by('id')).qs
return context
I found this to be much simpler to achieve pagination with filters on a given view:
class ProductView(ListView):
model = Product
template_name = 'products/products.html'
paginate_by = 5
context_object_name = 'products'
def get_queryset(self):
return Product.objects.filter(my_field=my_criteria)
Related
In a class based ListView, while rendering i want to dynamically change the no of items shown per page and the sorting order of those items.
I referred to this question and the answer by skoval00 works for me when i change the ordering and change the pages. But as soon as i change the no of items per page, my ordering is lost.
For ex: my generic view links to http://127.0.0.1:8000/marketplace/.
If i set the pagination to 10 items per page.
The url changes to http://127.0.0.1:8000/marketplace/?paginate_by=10
But now if i change my ordering too, i expect something like
http://127.0.0.1:8000/marketplace/?paginate_by=10&ordering=end_date
but i get
http://127.0.0.1:8000/marketplace/?ordering=end_date
And the pagination changes to the default value.
Same thing happens vice-versa. If i order first and then change pagination, then i lose the ordering once the pagination changes.
How do i preserve the first argument while adding the second one?
Here is the relevant class in views.py
class CatalogueListView(ListView):
model = Catalogue
context_object_name = 'catalogues'
template_name = 'marketplace/catalogue_list.html'
paginate_by = 5
ordering = ['end_date']
def get_queryset(self):
if self.request.GET.get('ordering'):
ordering = self.request.GET.get('ordering')
else:
ordering = 'end_date'
query_set = Catalogue.objects.filter(admin_approved=True, end_date__gt=timezone.now()).order_by(ordering)
return query_set
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.GET.get('paginate_by'):
context['paginate_by'] = self.request.GET.get('paginate_by')
else:
context['paginate_by'] = self.paginate_by
if self.request.GET.get('ordering'):
context['ordering'] = self.request.GET.get('ordering')
else:
context['ordering'] = self.ordering
return context
def get_paginate_by(self, queryset):
return self.request.GET.get('paginate_by', self.paginate_by)
ans
I am creating a Fixture webapp with Django. I have written the class below, which displays a list of first team fixtures. Can I rewrite it as TeamView, then pass the team_id?
class FirstView(generic.ListView):
template_name = 'fixtureapp/team.html'
context_object_name = 'fixtures'
def get_queryset(self):
"""return all first team matches"""
return Match.objects.filter(team_id=1).order_by('date')
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
data['page_title'] = '1st Team Fixtures'
return data
I have the following urls, how would I rewrite these to match?
urlpatterns = [
path('', views.HomeView.as_view(), name='home'),
path('first', views.FirstView.as_view(), name='first'),
path('second', views.SecondView.as_view(), name='second'),
as you can see I have currently created a second class called SecondView this is almost a carbon copy of FirstView, not very DRY
I can give you a brief of how it works, you can apply rest of the logic. Basic idea is to use slug.
In your html you can give the slug name with the url:
In urls.py, get that slug :
path('team/<slug:teamid_slug>/', views.TeamView.as_view(), name='team_by_id'),
Your Views should filter the query based on this slug, if no slug given it will give all the Match record. You can apply some other logic what fits to you.
class TeamView(generic.ListView):
queryset = Match.objects.all().order_by('date')
template_name = 'fixtureapp/team.html'
context_object_name = 'fixtures'
def get_queryset(self):
"""return all team_id team matches"""
return Match.objects.filter(team_id__slug=self.kwargs.get('teamid_slug')).order_by('date')
Also please have a look at this documentation of Dynamic Filtering
I'm trying to pass some parameters received from a submitted form (POST) to a ListView so that I can generate the relevant queryset based on those parameters.
I'm not clear if the parameters should be specified in the URL in addition to the View?
From current View:
area = form.cleaned_data['area']
service = form.cleaned_data['service']
usage = form.cleaned_data['usage']
return redirect('users:results', area=area, service=service, usage=usage)
urls.py
url(r'^results/$', views.ResultsView.as_view(), {}, name="results", ),
views.py
class ResultsView(ListView):
template_name = 'site/results.html'
paginate_by = 20
context_object_name = 'results'
def get_queryset(self, area, service, usage):
results = Results.objects.filter(area=area, service=service, usage=usage)
return results
My Goal
A site that list all my Updates (model) in a table
Dont display all models at once (pagination - maybe 10 per page)
Filter and sort the list
My thoughts
I can use ListView to get a set of all my Updates
Use paginate_by = 10
Use a form to set order_by or filter in my QuerySet
My Problem
I am not sure how to add an form to modify my QuerySet with filter and sortings. My Idea was to modify the Query in get_queryset with additional filter and order_by.
My View
class MyView(ListView):
model = Update
template_name = "updates/update.html"
paginate_by = 10
def get_queryset(self):
return Update.objects.filter(
~Q(state=Update.STATE_REJECTED),
~Q(state=Update.STATE_CANCELED),
~Q(state=Update.STATE_FINISHED),
).order_by(
'planned_release_date'
)
My Idea
Something like this. I know it's not working like this ... just to illustrate
class MyView(ListView):
model = Update
template_name = "updates/update.html"
paginate_by = 10
def post(self, request, *args, **kwargs):
new_context = Update.objects.filter(
request.POST.get("filter"),
).order_by(
request.POST.get("sorting"),
)
def get_queryset(self):
return Update.objects.filter(
~Q(state=Update.STATE_REJECTED),
~Q(state=Update.STATE_CANCELED),
~Q(state=Update.STATE_FINISHED),
).order_by(
'planned_release_date'
)
You don't need post. Pass the filter value and order_by in the url for example:
.../update/list/?filter=filter-val&orderby=order-val
and get the filter and orderby in the get_queryset like:
class MyView(ListView):
model = Update
template_name = "updates/update.html"
paginate_by = 10
def get_queryset(self):
filter_val = self.request.GET.get('filter', 'give-default-value')
order = self.request.GET.get('orderby', 'give-default-value')
new_context = Update.objects.filter(
state=filter_val,
).order_by(order)
return new_context
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['filter'] = self.request.GET.get('filter', 'give-default-value')
context['orderby'] = self.request.GET.get('orderby', 'give-default-value')
return context
Make sure you give proper default value to filter and orderby
Example form (you can modify this to your need):
<form method="get" action="{% url 'update-list' %}">
<p>Filter: <input type="text" value={{filter}} name="filter"/></p>
<p>order_by: <input type="text" value={{orderby}} name="orderby"/></p>
<p><input type="submit" name="submit" value="submit"/></p>
</form>
I am wondering why nobody mentioned here this cool library: django-filter https://github.com/carltongibson/django-filter
you can define your logic for filtering very clean and get fast working forms etc.
demo here: https://stackoverflow.com/a/46492378/953553
I posted this elsewhere but I think this adds to the selected answer.
I think you would be better off doing this via get_context_data. Manually create your HTML form and use GET to retrieve this data. An example from something I wrote is below. When you submit the form, you can use the get data to pass back via the context data. This example isn't tailored to your request, but it should help other users.
def get_context_data(self, **kwargs):
context = super(Search, self).get_context_data(**kwargs)
filter_set = Gauges.objects.all()
if self.request.GET.get('gauge_id'):
gauge_id = self.request.GET.get('gauge_id')
filter_set = filter_set.filter(gauge_id=gauge_id)
if self.request.GET.get('type'):
type = self.request.GET.get('type')
filter_set = filter_set.filter(type=type)
if self.request.GET.get('location'):
location = self.request.GET.get('location')
filter_set = filter_set.filter(location=location)
if self.request.GET.get('calibrator'):
calibrator = self.request.GET.get('calibrator')
filter_set = filter_set.filter(calibrator=calibrator)
if self.request.GET.get('next_cal_date'):
next_cal_date = self.request.GET.get('next_cal_date')
filter_set = filter_set.filter(next_cal_date__lte=next_cal_date)
context['gauges'] = filter_set
context['title'] = "Gauges "
context['types'] = Gauge_Types.objects.all()
context['locations'] = Locations.objects.all()
context['calibrators'] = Calibrator.objects.all()
# And so on for more models
return context
This is how we do it, that way you get validation/type conversion as well:
class UnitList(PermissionRequiredMixin, ListView):
""" Class based view to show a list of all buildings for a specific user """
model = Unit
ordering = ['building', 'unit']
paginate_by = 100
# Access
permission_required = ['core.manager_perm']
raise_exception = True # If true, give access denied message rather than redirecting to login
def get_queryset(self):
try:
units = self.model.objects.filter(building__company=self.request.user.profile.company)
except Profile.DoesNotExist:
units = self.model.objects.none()
form = UnitSearchForm(self.request.GET)
if form.is_valid():
filters = {}
address = form.cleaned_data['address']
neighborhood = form.cleaned_data['neighborhood']
beds = form.cleaned_data['beds']
amenity = form.cleaned_data['amenity']
if address:
filters['building__street_index__istartswith'] = compute_street_address_index(address)
if neighborhood:
filters['building__neighborhood__icontains'] = neighborhood
if beds:
filters['beds'] = beds
if amenity:
filters['unit_amenities__name__iexact'] = amenity
units = units.filter(**filters)
return units.select_related('building').order_by(*self.ordering)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'] = UnitSearchForm(self.request.GET)
return context
I have a page that should show an event and the presentations of this event. In my code they are not related (I still have to fix that). On the home page, which receives the event and the lectures, the view is like this:
views.py
class EventoView(ListView):
model = Evento
template_name = 'home.html'
context_object_name = 'evento_list'
queryset = Evento.objects.all().order_by('-data')[:1]
class RegistroView(ListView):
model = Registro
template_name = 'home.html'
context_object_name = 'registro_list'
queryset = Registro.objects.all()
The problem is that I can only pass the Event object, the object of Registration, which show lectures Indexed must also be passed, however, only accepts a Django view for url.
urls.py
urlpatterns = patterns('',
url(r'^$', EventoView.as_view(), name='home'), #I can't pass two views
url(r'^cadastro/', CriarRegistroView.as_view(), name='cadastro'),
url(r'^contato/', CriarContatoView.as_view(), name='contato'),
url(r'^sobre/', SobreView.as_view(), name='sobre'),
url(r'^admin/', include(admin.site.urls)),
)
How can I solve this problem?
Thanks.
it looks like you can override ListView.get_context_data
class RegistroView(ListView):
model = Evento
def get_context_data(self, **kwargs):
context = super(RegistroListView, self).get_context_data(**kwargs)
context['registros'] = Registro.objects.all()
context['eventos'] = Evento.objects.all().order_by('-data')[:1]
return context
I don't have experience with ListViews so i don't know if i am using it as it is supposed to be used, or not