Reuse queryset in multiple views - django

I am building a application which has several views - HomePageView, SearchPageView and DetailPageView. The aforementioned views return the same queryset. My question is, what is the proper way to define let's say a "global" queryset which would be then used in multiple Views.
To illustrate my point here is an example of what I have:
class HomePageView(TemplateView):
def get_queryset():
return Systemevents.objects.filter(**filter)
class SearchPageView(ListView):
def get_queryset():
return Systemevents.objects.filter(**filter)
class LogDetailView(DetailView):
def get_queryset():
return Systemevents.objects.filter(**filter)
What I would like to achieve:
global queryset = Systemevents.objects.filter(**filter)
class HomePageView(TemplateView):
def get_queryset():
return queryset
class SearchPageView(ListView):
def get_queryset():
return queryset
class LogDetailView(DetailView):
def get_queryset():
return queryset
Thanks in advance,
Jordan

you can use something called manager for that for reusing that query set you have to specify all your logic inside the django manager so you don't have to go for a long query set just small readable query set checkout more about in the django documentation
https://docs.djangoproject.com/en/3.1/topics/db/managers/

Related

Trying to filter by user group using class based view with django-tables2, can't access self.user

I'm trying to use a class based view using django-tables2 to define the table and template returned based on what group the logged in user belongs to.
This is my attempt at doing so:
class cases(LoginRequiredMixin, SingleTableView):
login_url = '/account/login/'
if User.objects.filter(pk=self.request.user.id, groups__name='teachers').exists():
model = Graduation
table_class = TeachersTable
template_name = 'mysite/teachers.html'
elif User.objects.filter(pk=self.request.user.id, groups__name='students').exists():
model = Graduation
table_class = StudentsTable
template_name = 'mysite/students.html'
I think the approach is more or less correct (I've only learned about class based views today), but I am unsure how to access the user id.
The relevant parts of this view should only be called when a user is logged in (at least I think) because I'm using the LoginRequiredMixin, so 'self' should exist.
The answers I have seen addressing this problem say override get_queryset, but I am reluctant to do that as I think that will break django-tables2.
What is the best approach in this case to do what I am trying to do?
There are a few things going on here.
First, all code in a class runs when the module is loaded, not when the view is run. So your code is running at the wrong time. Partially because of this, you don't have access to self.
self isn't that magic, it doesn't appear from nowhere, you can only use it in class methods:
class Foo:
self.spam = "eggs" # Wrong, no self here.
class Bar:
def set_spam(self): # self is the instance of the class.
self.spam = "eggs" # Works.
I'm not sure what table_class is, from a search it looks like it comes from django-tables, so there's a bit of guesswork here. It seems like you want something like this:
class GraduationCaseView(LoginRequiredMixin, SingleTableView):
model = Graduation
def get_template_names(self):
if self.request.user.groups.filter(name='teachers').exists():
return ['mysite/teachers.html']
return 'mysite/students.html'
def get_table_class(self):
if self.request.user.groups.filter(name='teachers').exists():
return TeachersTable
return StudentsTable
This should work. There is an issue with this: you'd be doing the same database query twice. There are some ways around this, but it requires knowing a bit about CBVs and their execution order. Since I'm not sure what SingleTableView is doing, this may or may not work:
class GraduationCaseView(LoginRequiredMixin, SingleTableView):
model = Graduation
def get_queryset(self):
qs = super().get_queryset()
if self.request.user.groups.filter(name='teachers').exists():
self.group = 'teachers'
else:
self.group = 'students'
return qs
def get_template_names(self):
if self.group == 'teachers':
return ['mysite/teachers.html']
return 'mysite/students.html'
def get_table_class(self):
if self.group == 'teachers':
return TeachersTable
return StudentsTable
You should probably read up on Python's documentation of classes, too, so you understand how they work.
One more thing, you don't need to set login_url on your view if settings.LOGIN_URL is set.

adding detail information to django listview object in template

I have a listview in which I'm hoping to insert additional details about the object (activity duration and average power) in the same row as the link to the object detail (the best way to describe it would be that I want some detailview attributes inserted into the listview). At the moment, the best I can achieve is a separate context dictionary listed below the object_list, as shown in this screen shot:
And the following is my listview:
class RideDataListView(LoginRequiredMixin, ListView):
model = RideData
context_object_name='object_list'
template_name='PMC/ridedata_list.html'
def get_queryset(self):
queryset = super(RideDataListView, self).get_queryset()
return queryset
def get_context_data(self, *args, **kwargs):
model = RideData
context = super(RideDataListView, self).get_context_data(*args, **kwargs)
records = list(RideData.objects.all().values())
actdict2={}
id=[]
ap=[]
actdur=[]
for record in records:
actdf=pd.DataFrame.from_dict(record)
id.append(actdf['id'].iloc[0])
ap.append(actdf['watts'].mean())
actdur.append(str(timedelta(seconds=len(actdf['time']))))
actdf2=pd.DataFrame()
actdf2['id']=id
actdf2['ap']=ap
actdf2['actdur']=actdur
actdict2=actdf2.to_dict('records')
context['actdict']=actdict2
context['actdur']=actdur
return context
What I haven't been able to nail down in my research is if there is a way to either a) annotate the queryset with stuff from context or b) loop through the context dictionary 'actdict' within the object_list loop (doesn't seem possible based on some attempts) or c) include individual lists (ap and actdur as additions to to query. Just curious for some additional leads to add some more object detail to the basic listview.
Your context is intended to contain your data, but the way it is displayed rely on the HTML template you will use : https://docs.djangoproject.com/en/3.0/topics/templates/
The actual solution to this was to add to queryset object within def get_queryset
def get_queryset(self):
queryset = super(RideDataListView, self).get_queryset()
for obj in queryset:
record=list(obj.watts)
actdf=pd.DataFrame()
actdf['watts']=record
obj.actdur=str(timedelta(seconds=len(actdf['watts'])))
obj.ap=actdf['watts'].mean()
return queryset
This returned the additional summary information I wanted to include in the listview that is also used in detailview

how to chain queryset mixin for django rest viewsets?

I need to write the following querysets mixins:
class VendorOwnedQuerySetMixin(models.QuerySet):
def get_objects_for_vendor(self, request):
vendor_user = VendorUserModel.objects.get(user=request.user)
return qs.filter(vendor=vendor_user.vendor)
class OrganizationOwnedQuerySetMixin(object):
def get_objects_for_organization(self, request):
return self.filter(organization__domains__name=hostname_from_request(request))
All's working well because some model managers will inherit the first mixin and some inherit the second.
Then inside the get_queryset of the viewset, i will call the appropriate get_objects method.
example
def get_queryset(self, queryset=None):
return Some.objects.get_objects_for_organization(self.request)
Now I need to have a django rest viewset that needs to run the get_queryset method that runs both filters.
How do I "chain" them within the get_queryset method? Because I want to reuse my code where possible
In order to chain the filters, you need to get the previous queryset. This can be achieved by calling super().get_queryset(request). It will get the queryset from the other classes your view inherit from and will apply the filter:
class VendorOwnedQuerySetMixin(models.QuerySet):
def get_queryset(self, request):
qs = super().get_queryset(request)
vendor_user = VendorUserModel.objects.get(user=request.user)
return qs.filter(vendor__user=vendor_user.vendor)
class OrganizationOwnedQuerySetMixin(object):
def get_objects_for_organization(self, request):
qs = super().get_queryset(request)
return qs.filter(organization__domains__name=hostname_from_request(request)
Remember that you MUST set the mixins before the view in order to work. For example:
class MyView(OrganizationOwnedQuerySetMixin, VendorOwnedQuerySetMixin, RetrieveAPIView):
...
A call on get_queryset will get the RetrieveAPIView queryset which will get passed to VendorOwnedQuerySetMixin once the super() call returns, get the filter applied and returns the result to OrganizationOwnedQuerySetMixin after the super() is called which in turn will apply its filter and return the result.

How to call a function with context in django CB list view?

This is my view:
class viewbloglistview(LoginRequiredMixin,ListView):
model = Blog
paginate_by = 6
def get_template_names(self):
if True:
return ['blog/view_blogs.html']
else:
return ['blog/blog_list.html']
def get_queryset(self):
return Blog.objects.all().order_by('-blog_views')[:20]
def get_context_data(self, **kwargs):
context = super(viewbloglistview, self).get_context_data(**kwargs)
context['categories_list'] = categories.objects.all()
return context
This is my function in models.py file:
def categories_count(self):
categories_count = categories.objects.annotate(blog_count=Count('blogs')).values_list('Title','blog_count')
return categories_count
I want call the function in my views with a context name to render the activity in my template..
Can anyone please help me out to solve this problem??
Thank you
This is a python problem, your question is unclear but based on what you said:
Case the function in in your model.py alone:
from . import model.py
// code
categories_count()
Case the function is a method in a class as it is shown on you code with the self parameter in it:
from . import model.py
// code
classname.categories_count()
Assuming that you have named your class as 'categories' (which should have been named as Category in the first place),
categories_count should have been in a manager as you are querying in a class level. Say you don't want a manager and want to keep the code inside the model, then you can use it as a class method.
#classmethod
def categories_count(cls):
return cls.objects.annotate(blog_count=Count('blogs')).values_list('Title','blog_count')
and in the views use it as
categories.categories_count()
Just remember that the regular methods with the 'self' argument like the one you have, should only be used when you are dealing with a single instance, not when you are accessing the class itself.

Django how to write DRY views

I have many views that call the same functions every time and I wonder before I go ahead with this approach if anyway I can make it more DRY.
For example I have many pages on a site that have on the left side menu a list of the same articles and photos. So on each of my views I do the following:
context_dict = {'articles': get_articles(blogger.id), 'photos': get_photos(blogger.id)}
return render_to_response('...', context_dict, context)
It must exist a way that I don't have to repeat myself every time since they are required on 90% of the pages.
The issue of repeating view functionality is part of why many people like class-based views. You could implement a method that adds those variables to the base class, and then have other views inherit from that one, or provide a standardized "render" method. For example:
class BaseView(View):
template = 'public/base_template.html'
def get(self, *args, **options):
return render_to_response(self.template, self.render_view())
def render_view(self, *args, **options):
context = {"photos": Photo.objects.all()}
return context
class OtherView(BaseView):
template = 'public/other_template.html'
def render_view(self, *args, **options):
context = super(OtherView, self).render_view(*args, **options)
context['additional_context'] = True
return context
...or something similar. Then, you don't have to worry about calling render with variables that are already included.
I can think of a few ways to accomplish this with function-based views, but I think class-based lends itself very well to DRY principles, so I thought I'd spread the gospel :)
https://docs.djangoproject.com/en/1.9/topics/class-based-views/intro/
You mean something like
def get_extra_context(blog_id):
return {'articles': get_articles(blogger.id), 'photos': get_photos(blogger.id)}
A call to get_extra_context has to be made in every view of course.
As Robert Townley says, class based views are very helpful for keeping to DRY principles. I often use some simple mixins to share some logic between different views. Your class based views can then inherit from this mixin if they need this functionality. For example:
class BloggerMixin(object):
articles_context_name = 'articles'
photos_context_name = 'photos'
def get_blogger(self):
""" I'm just assumming your blogger is the current user for simplicity.
(And I'm assuming they're logged in already)"""
return self.request.user
def get_articles(self):
return Article.objects.filter(blogger=self.get_blogger())
def get_photos(self):
return Photo.objects.filter(blogger=self.get_blogger())
def get_context_data(self, **kwargs):
context = super(BloggerMixin, self).get_context_data(**kwargs)
context[self.articles_context_name] = self.get_articles()
context[self.photos_context_name] = self.get_photos()
return context
This would allow you to inherit this extra functionality on class based views that need it:
class ExampleView(BloggerMixin, ListView):
model = SomeOtherModel
Our very simple ExampleView class will now have a list of Article, Photo and SomeOtherModel in its context.