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.
Related
If I want to override a method in my viewset to change the serializer_class for only a single method, how can I do that.
I tried passing serializer_class=CustomSerializer but it doesn't have any effect.
class VoteViewset(viewsets.ModelViewSet):
queryset = Vote.objects.all()
# Use normal serializer for other methods
serializer_class = VoteSerializer
def list(self, request, *args, **kwargs):
# Use custom serializer for list method
return viewsets.ModelViewSet.list(self, request, serializer_class=VoteWebSerializer, *args, **kwargs)
Basically do the same list method as the inherited viewset, but use a different serializer to parse the data.
The main reason for this is because javascript does not handle 64 bit integers, so I need to return the BigInteger fields as a string instead of integer.
Override the get_serializer_class(...) method as
class VoteViewset(viewsets.ModelViewSet):
queryset = Vote.objects.all()
def get_serializer_class(self):
if self.action == "list":
return VoteWebSerializer
return VoteSerializer
What JPG answered is the correct way. Another thing you could do is overriding the list method like you are doing but modifying the default behavior using the serializer that you want.
It would be something like:
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
You can check the default methods in the source code or I recommend using Classy DRF. For example here you can see what DRF is doing in the list method of a ModelViewSet doing and use that as a starting point.
I have a class-based view that I use to obtain a queryset and pass to django-tables2 which renders the result. That aspect all works fine. I am trying to pass a record instance from a different queryset to the template, so I can display information above the table django-tables2 produces.
Upon searching, it seems the 'right' way to do so is via the get_context_data method. However when I attempt do add this method to my view, simply obtaining the queryset and returning it, it produces an error Expected table or queryset, not str. I isolated this to being due to {% render_table table %} in my template. Without that, I can access my 'team' object as intended.
Why is this happening? The qs queryset was being passed fine to django-tables2 before I added my get_context_data method. Does the qs queryset have to be returned via get_context_data as well? If so, why?
This is my attempt:
class myteam(LoginRequiredMixin, SingleTableView):
def get_queryset(self):
qs = Contestant.objects.filter(assigned_team=self.request.user.contestant.assigned_team)
qs = qs.exclude(id=self.request.user.contestant.id)
return qs
def get_template_names(self):
return 'xgames/viewteam.html'
def get_table_class(self):
return TeamsTable
def get_context_data(self):
team = Team.objects.get(id=self.request.user.contestant.assigned_team.id)
return {"team": team}
seems like you forgot to call the super() method
class myteam(LoginRequiredMixin, SingleTableView):
# Rest of the code
def get_context_data(self):
context = super().get_context_data()
context["team"] = Team.objects.get(
id=self.request.user.contestant.assigned_team.id
)
return context
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/
Might seem like a dumb question but trying to pass additional filter variables to a ModelViewSet but request.data is empty.
class ObjViewSet(viewsets.ModelViewSet):
def get_queryset(self):
if self.request.get('orderBy', None):
return Obj.objects.all().order_by(self.request.get('orderBy'))
else:
return Obj.objects.all()
What is the correct way to do this? I don't want to screw up the /view/<id>/ routing but I also wish to pass a couple more variables via /view/?orderBy=id&var2=val2
Using DefaultRouter
router.register('objs', views.ObjViewSet, basename="obj")
You should change the self.request.get('orderBy') into self.request.GET.get('orderBy')
class ObjViewSet(viewsets.ModelViewSet):
queryset = Obj.objects.all()
def get_queryset(self):
order_by = self.request.GET.get('orderBy')
if order_by is not None:
return self.queryset.order_by(order_by)
return self.queryset
I am implementing csv export on django. In particular I have a link on my template to export actual query and for this I am trying to handle all on the same class based view.
Here is my code
# views.py
class MyView(ListView):
template_name = 'my_template.html'
model = Archivio
def get_queryset(self):
if self.request.GET.get('q'):
dateC = '01/01/'+self.request.GET.get('q')
queryset = Archivio.objects.filter(~Q(quoteiscrizione__anno_quota__exact=self.request.GET.get('q'))
return queryset
# my custom method
#staticmethod
def csv_output():
qs = MyView.get_queryset(): # i want to access to the queryset variable from get_queryset() method of the class
# here i have other code to produce csv output
But the method csv_output() is wrong.. I get this TypeError: csv_output() takes 0 positional arguments but 1 was given
I have also tried with a classmethod decorator but without success.
My question is: How can i access queryset variable from another method of the same class?
Note the self in get_queryset(self). It means the first argument of this method is always the instance of the class (in your case, MyView). So when you want to call it in another method, you should provide this instance.
The solution is to replace your #staticmethod decorator with the #classmethod one:
class MyView(ListView):
template_name = 'my_template.html'
model = Archivio
def get_queryset(self):
if self.request.GET.get('q'):
dateC = '01/01/' + self.request.GET.get('q')
queryset = Archivio.objects.filter(~Q(quoteiscrizione__anno_quota__exact=self.request.GET.get('q'))
return queryset
#classmethod
def csv_output(cls):
qs = cls.get_queryset(cls)
# Class methods have one required positional argument
# which is the class that contains them
# They are called like this:
# MyClass.class_method()
# So in your case, you can call
# get_queryset() by doing MyView.get_queryset()
# But as you are in a class method, you do
# cls.get_queryset()
# As get_queryset() needs one positional argument *self*,
# which is the instance of your class, you do
# cls.get_queryset(cls)
# and it will work as expected :-)
I solved my Issue saving my queryset on the request and then using it somewhere on my views like this:
views.py
class SomeClass(ListView):
def get_queryset(self):
.....
queryset = MyModel.objects.filter(*somefilterlist)
self.request.session['search_queryset'] = serialize('json', queryset) # in order to save something on session in must be JSON serialized
class Output(SomeClass):
#classmethod
def cvsOutput(cls):
deserialized = list(deserialize('json', request.session.get('search_queryset')))
pk_list = []
for arch in deserialized:
pk_list.append(arch.object.pk) # List of pk
queryset = Archivio.objects.filter(pk__in=pk_list) # Query the list ok pk's
In this way I was able to make available the list ok pk of the queryset of my ListView, then I make again the same query based on that list of pk's..