where to include business logic in listview Class methods - django

I am trying to understand Django's class based views (very new to it), especially, ListView. I am struggling to understand where the "business logic should go". Say for example, I have the following class:
#views.py
class DisplayListView(ListView):
model = Cars
template_name = "searchres_list.html"
paginate_by = '5'
context_object_name = "titles"
def get_context_data(self, **kwargs):
context = super(SearchDisplayListView, self).get_context_data(**kwargs)
# custom logic whoch spits out "now". in this example [1 -->10]
context['now'] = [1,2,3,4,5,6,7,8,9,10]
return context
It works fine and I am able to look the [1 --> 10] on my template. However, when I look at the methods available under ListView I see that I could probably include my logic in get_queryset method. So, something like:
def get_queryset(self):
# Fetch the queryset from the parent get_queryset
queryset = super(SearchDisplayListView, self).get_queryset()
# custom logic whoch spits out "now". in this example [1 -->10]
queryset = [1,2,3,4,5,6,7,8,9,10]
return queryset
So, my rather (stupid) question is (or have I got this all completely wrong!), where should the business logic ideally go:
def get_context_data
def get_queryset
Thanks for your time.

Probably the best answer to such a subjective question will be: it depends.
My personal algorithm for dealing with the situations like this is the following:
if you need to add something to the context that will be passed to the template, then you don't have a choice actually, because in get_queryset method you can only modify the queryset for your ListView. So I use get_context_data in this case.
but if you're going to perform some dynamic queryset modifications, let's say your view can operate on similar model classes and the actual class is determined by the arguments passed into the view, then probably you need to overwrite get_queryset method.
Hope I gave you some insights on the topic :)

Related

Limiting the queryset RelatedFields to only currently related objects

I am overriding the get_queryset function in serializers.PrimaryKeyRelatedField to something like this achieve what I described in the title:
def get_queryset(self):
queryset = self.queryset
if self.root.instance:
return queryset.filter(**{str(self.root.instance._meta.verbose_name_plural) :
self.root.instance})
else:
return queryset
This feels like an extremely hacky way to achieve it. Is there a cleaner syntax for getting related objects given a queryset and a root instance? I want the function to be generic so I can use it elsewhere. Or is there a rest framework's recommended way of setting queryset?

django - How can I filter in serializer

class User(generics.RetrieveAPIView):
serializer_class = RetrieveLocalSerializer
queryset = User.objects.filter(
fields_1=True,
fields_2=False
)
class LocalSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('field_1', 'field_2', 'field_3',)
The API did not work as it I wish. When I tried get user that does not have the property i want, it still returned the result.
I even tried override that function but it did not work too.
def get_queryset(self):
return User.objects.filter(
is_localguide=True,
state=PROFILE_STATE.PUBLISHED
)
Any help is appreciated.
If I understood your question correctly you wish to get list of instances in your view (using Django Rest Framework). The problem is that your view is inheriting from a generics.RetrieveAPIView. This view class calls self.retrieve(request, *args, **kwargs) method which returns you an object, not queryset. I think that you should inherit your view from a ListAPIView class. This class inherits ListModelMixin which
Provides a .list(request, *args, **kwargs) method, that implements listing a queryset.
So your code will be looking like this:
class User(generics.ListAPIView):
serializer_class = RetrieveLocalSerializer
queryset = User.objects.filter(
fields_1=True,
fields_2=False
)
See http://www.django-rest-framework.org/api-guide/generic-views/#listapiview for more information.
You may either define your queryset in a view or override get_queryset method:
queryset - The queryset that should be used for returning objects from this view. Typically, you must either set this attribute, or override the get_queryset() method. If you are overriding a view method, it is important that you call get_queryset() instead of accessing this property directly, as queryset will get evaluated once, and those results will be cached for all subsequent requests.
You may find more information here: http://www.django-rest-framework.org/api-guide/generic-views/#genericapiview
Hope this will help)

Passing URL variables to a class based view

I have just started messing with class based views and I would like to be able to access variables from the URL inside my class. But I am having difficulties getting this to work. I saw some answers but they were all so short I found them to be of no help.
Basically I have a url
url(r'^(?P<journal_id>[0-9]+)/$',
views.Journal_Article_List.as_view(),
name='Journal_Page'),
Then I would like to use ListView to display all articles in the particular journal. My article table however is linked to the journal table via a journal_id. So I end up doing the following
class Journal_Article_List(ListView):
template_name = "journal_article_list.html"
model = Articles
queryset = Articles.objects.filter(JOURNAL_ID = journal_id)
paginate_by = 12
def get_context_data(self, **kwargs):
context = super(Journal_Article_List, self).get_context_data(**kwargs)
context['range'] = range(context["paginator"].num_pages)
return context
The journal_id however is not passed on like it is in functional views. From what I could find on the topic I read I can access the variable using
self.kwargs['journal_id']
But I’m kind of lost on how I am supposed to do that. I have tried it directly within the class which lets me know that self does not exist or by overwriting get_queryset, in which case it tells me as_view() only accepts arguments that are already attributes of the class.
If you override get_queryset, you can access journal_id from the URL in self.kwargs:
def get_queryset(self):
return Articles.objects.filter(JOURNAL_ID=self.kwargs['journal_id'])
You can read more about django’s dynamic filtering in the docs.

Query multiple models with class-based views

I would like to solve the following situation.
I have a side panel containing information of the active user. For this an instance of UserInfo model needs to be passed to the views.
Additionally, I would like to pass a number of other model instances to the pages (eg. Purchases, Favourites, etc.).
I know this is pretty easy to do by overriding the get_context_data.
def get_context_data(self, **kwargs):
kwargs['purchases'] = Purchases.objects.get(id=1)
kwargs['favourites'] = Favourites.objects.get(id=1)
.... etc
return super(UploadFileView, self).get_context_data(**kwargs)
So my question is - what would be the best/most appropriate CBV to use for this?
This isn't quite a DetailView as you have multiple objects, but it isn't a ListView either, nor does it look like a FormView or its children.
Since you gain nothing from those, a simple TemplateView is probably the way to go.
If you are querying the same UserInfo, Purchases, Favorites, etc in multiple views, create a Mixin that you can re-use.
class CommonUserInfoMixin (object):
def get_context_data(self, **kwargs):
context = super(OrgContextMixin, self).get_context_data(**kwargs)
... # Add more to context object
Then you can use this in your normal List, Detail, Update, etc CBV's
class ItemList(CommonUserInfoMixin, ListView):
....

Django model queryset filter as a function

I'm trying to create a custom function that I can place in a queryset "chain" that will apply a filter to the queryset. Like with normal Django queryset filters, it will take the queryset to the left of it and pass the resulting queryset to the right.
Before adding my custom function to the queryset chain:
models.MyModel.objects.all()
models.MyModel.objects.some_manger_function()
models.MyModel.objects.some_manger_function().count()
models.MyModel.objects.some_manger_function().filter(title='something')
After adding my custom function to the queryset chain:
models.MyModel.objects.all().my_custom_filter()
models.MyModel.objects.some_manger_function().my_custom_filter()
models.MyModel.objects.some_manger_function().my_custom_filter().count()
models.MyModel.objects.some_manger_function().my_custom_filter()\
.filter(title='something')
I'm not sure how to construct my function to do this. Does it need some sort of decorator around it?
???? def my_custom_filter(???):
???? return qs.filter(id__gte=10)
Does anyone know a good way to accomplish this?
The following might work, but I was hoping for something a little more Django-like.
def my_custom_filter(qs):
return qs.filter(id__gte=1)
my_custom_filter(models.MyModel.objects.all()).count()
Any advice is much appreciated.
Thanks,
Joe
UPDATE: I'm trying to work out the details of Ignacio's solution. I've not done too much with QuerySet overriding so I'm piecing together what I'm able to find...
class MyQuerySet(QuerySet):
def filter(self, *args, **kwargs):
return super(self.__class__, self).filter(*args, **kwargs).\
filter(id__gt=5)
class MyManager(models.Manager):
def testqs(self):
return MyQuerySet(self.model)
However, I don't think this is working the way I expect. Any suggestions?
>>> models.MyModel.objects.testqs().filter()
UPDATE 2:
This article proved to be useful.
http://zmsmith.com/2010/04/using-custom-django-querysets/
You will need to write your own QuerySet child class with the method added, then use that class in the manager.