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
Related
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.
code which not working
this code is not working, i don't know what is wrong in my code.
Views.py
You didn't define the model name on the view.Add the model name and try to add custom context_object_name. For reference check this
class BookListView(generic.ListView):
model = Book
context_object_name = 'my_book_list' # your own name for the list as a template variable
queryset = Book.objects.filter(title__icontains='war')[:5] # Get 5 books containing the title war
template_name = 'books/my_arbitrary_template_name_list.html' # Specify your own template name/location
You have to specify which model to create ListView. Here in your case define model=Post as
from django.views.generic.list import ListView
class PostList(ListView):
model = Post
template_name = 'blog/index.html'
queryset = Post.objects.filter(status=1).order_by("-created_on")
Or you can also use get_queryset() as
from django.views.generic.list import ListView
class PostList(ListView):
# specify the model
model = Post
template_name = 'blog/index.html'
def get_queryset(self, *args, **kwargs):
qs = super(PostList, self).get_queryset(*args, **kwargs)
qs = qs.filter(status=1).order_by("-created_on")
return qs
I don't think I am implementing this correctly, but I am trying to change the serializer used for a queryset based on a condition (if there are no venues in one queryset, switch to another serializer and just return list object). I'm not quite sure how to do this.
Here is the view
class SavedVenuesViewSet(viewsets.ModelViewSet):
serializer_class = UserVenueSerializer
def get_queryset(self):
list_id = self.request.GET.get('list_id', None)
user = self.request.user.id
print(user)
print(list_id)
print(type(list_id))
qs = UserVenue.objects.filter(user_list=int(float(list_id)))
if not qs:
print("EMPTY LIST") #this is where i try to switch serializer
serializer_class = UserListSerializer
return UserVenue.objects.filter(id=int(float(list_id)))
else:
return qs
Here are the relevant serializers:
class UserVenueSerializer(serializers.ModelSerializer):
venue = mapCafesSerializer()
class Meta:
model = UserVenue
fields = ['user', 'user_list', 'venue']
depth = 2
[...]
class UserListSerializer(serializers.ModelSerializer):
class Meta:
model = UserList
fields = ['id', 'user', 'list_name']
depth = 2
The traceback isn't throwing an error but it isn't doing what I am hoping:
1
45
<class 'str'>
EMPTY LIST
[29/Sep/2021 11:05:36] "GET /api/savedvenues/?list_id=45 HTTP/1.1" 200 2
This is the correct practice to change serializer class in ModelViewSet:
You have to override get_serializesr_class method:
class SavedVenuesViewSet(viewsets.ModelViewSet):
serializer_class = UserVenueSerializer
def get_serializer_class(self):
if not self.get_queryset(): # Check your conditions here
return UserListSerializer
else:
return UserVenueSerializer
You could remove serializer_class field from your view, and create get_serializer_class() method, that will contain the logic for choosing the serializer. Is the best practice.
But, you can also, do it in your get queryset.
First, remove serializer_class attribute from your view. Instead, set it inside your get_queryset method (with this.serializer_class);
def get_queryset(self):
list_id = self.request.GET.get('list_id', None)
qs = UserVenue.objects.filter(user_list=int(float(list_id)))
if not qs:
self.serializer_class = UserVenueSerializer
return UserVenue.objects.filter(id=int(float(list_id)))
else:
self.serializer_class = UserListSerializer
return qs
Don't forget use the self.serializer_class.
According to the documentation,
The name of the URLConf keyword argument that contains the slug. By default, slug_url_kwarg is 'slug'.
How do I use this for an UpdateView where I have two slugs book_slug and chapter_slug? According to this answer, I can override get_queryset method. But is there a way to use multiple slugs with slug_url_kwarg? I tried slug_url_kwarg = ['book_slug', 'chapter_slug'] but it doesn't work.
Edit:
I ended up overriding the get_object method.
No, slug_url_kwarg is designed to take a single slug, not multiple.
You can however make your own mixin if you want to be able to do this.
class MultiSlugMixin:
pk_url_kwarg = 'pk'
slug_url_kwargs = {'slug': 'slug'} # {slug_field: slug_url_kwarg}
query_pk_and_slug = False
def get_object(self, queryset=None):
if queryset is None:
queryset = self.get_queryset()
pk = self.kwargs.get(self.pk_url_kwarg)
slugs = {
field: self.kwargs[url_kwarg]
for field, url_kwarg in self.slug_url_kwargs.items()
}
if pk is not None:
queryset = queryset.filter(pk=pk)
if slugs and (pk is None or self.query_pk_and_slug):
queryset = queryset.filter(**slugs)
if pk is None and not slugs:
raise AttributeError(
"Generic detail view %s must be called with either an object "
"pk or a slug in the URLconf." % self.__class__.__name__
)
try:
obj = queryset.get()
except queryset.model.DoesNotExist:
raise Http404(_("No %(verbose_name)s found matching the query") %
{'verbose_name': queryset.model._meta.verbose_name})
return obj
class ChapterUpdateView(MultiSlugMixin, UpdateView):
model = Chapter
slug_url_kwargs = {'slug': 'chapter_slug', 'book__slug': 'book_slug'}
I've got two models:
class Parent:
...
class Child:
parent = models.ForeignKey(Parent)
In the model admin of the Parent I want to show an inline of the Child with a custom queryset, not only the ones related to the parent through the fk field.
I've tried:
class ChildInline(admin.TabularInline):
model = Child
def get_queryset(self, request):
return Child.objects.filter(<my custom filter>)
class ParentAdmin(admin.ModelAdmin):
inlines = [ChildInline]
But still the only children shown in the inline are the ones that fullfill both filters: related to the parent by the FK + my custom filter.
Is it possible to do this?
EDIT:
I've seen now is the BaseInlineFormSet who is filtering the queryset I compose to keep only childs related to the parent, any idea how to avoid this?
django/forms/models.py
class BaseInlineFormSet(BaseModelFormSet):
...
if self.instance.pk is not None:
qs = queryset.filter(**{self.fk.name: self.instance})
...
You have to override __init__() method of BaseInlineFormSet and update queryset there.
from django.forms.models import BaseInlineFormSet
class ChildInlineFormSet(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(ChildInlineFormSet, self).__init__(*args, **kwargs)
# Now we need to make a queryset to each field of each form inline
self.queryset = Child.objects.filter(<my custom filter>)
Then initialise formset attribute with ChildInlineFormSet
class ChildInline(admin.TabularInline):
model = Child
formset = ChildInlineFormSet
extra = 0
The old answer doesn't work anymore for current Django 2.2 or 3 because self.queryset get ignored
Current solution is to override the get_queryset:
from django.forms.models import BaseInlineFormSet
class ChildInlineFormSet(BaseInlineFormSet):
def get_queryset(self):
qs = super(ChildInlineFormSet, self).get_queryset()
return qs.filter(<custom query filters>)
class ChildInline(admin.TabularInline):
model = Child
formset = ChildInlineFormSet
extra = 0