Django tells that the object with given slug could not be found (i.e. 404 code). Though queryset returned isn't empty
class PollDetailView(RetrieveAPIView):
serializer_class = PollSerializer
def get_queryset(self):
slug = self.kwargs['pk']
print(Poll.objects.filter(slug=slug)) # Prints '<QuerySet [<Poll: ddd>]>' reaching '/api/poll/ddd/' url
return Poll.objects.filter(slug=slug) # 404 Not Found
add look_up field in your PollDetailView
look_up = 'slug'
and in your urls.py
change PollDetailView url to
url(r'^api/polls/(?P<slug>[\w-]+)/$', views.PollDetail.as_view(),name='poll-detail'),
Related
I am trying to add some annotation in authenticated User using rest-auth
Here is my code in serializers.py
class CustomUserSerializer(UserDetailsSerializer):
test = serializers.IntegerField()
class Meta:
model=User
fields='__all__'
And here is my code in views.py
class CustomUserView(UserDetailsView):
queryset= User.objects.annotate(test=Sum('logs__work_hours'))
serializer_class = CustomUserSerializer
but I am having this error after running the system
**
Got AttributeError when attempting to get a value for field test on
serializer CustomUserSerializer. The serializer field might be named
incorrectly and not match any attribute or key on the User instance.
Original exception text was: 'User' object has no attribute 'test'.
**
In this context, queryset is ignored.
Why? In your program, you're subclassing UserDetailsView from django-rest-auth. That implements get_object() like so:
def get_object(self):
return self.request.user
With no reference to the queryset variable.
You need to override get_object() in CustomUserView, copying the above implementation, and setting a test attribute on the user.
def get_object(self):
user = self.request.user
user.test = ... # put query to get value of test here
return user
I am learning class based views in Django. I was reading the Django documentation and read about queryset attribute and the get_queryset() method. When googled them I came across this answer.
I tried to replicate the result using my code:
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
return Question.objects.order_by('-pub_date')[:2]
class IndexView2(generic.ListView):
template_name = 'polls/index2.html'
context_object_name = 'latest_question_list2'
queryset = Question.objects.all()
In answer it is mentioned that when you set queryset, the queryset is created only once, when you start your server. On the other hand, the get_queryset method is called for every request.
But I was able to insert questions in database and they were available in the page index2.html without restarting, I was able to change the database and changes were reflected on the page index2.html after refreshing the page.
I further googled and found this link. In the DRF website, it is mentioned that queryset will get evaluated once, and those results will be cached for all subsequent requests.
Can you point where I am going wrong ? What link I am missing ?
A QuerySet is evaluated once, but the default implementation of get_queryset, will use queryset.all(), thus each time constructing a new queryset that will force reevaluation.
Indeed, the implementation of the .get_queryset(…) method [GitHub] works with:
def get_queryset(self):
if self.queryset is not None:
queryset = self.queryset
if isinstance(queryset, QuerySet):
queryset = queryset.all()
elif self.model is not None:
queryset = self.model._default_manager.all()
else:
raise ImproperlyConfigured(
"%(cls)s is missing a QuerySet. Define "
"%(cls)s.model, %(cls)s.queryset, or override "
"%(cls)s.get_queryset()." % {
'cls': self.__class__.__name__
}
)
ordering = self.get_ordering()
if ordering:
if isinstance(ordering, str):
ordering = (ordering,)
queryset = queryset.order_by(*ordering)
return queryset
THis thus means that we each time make a new "copy" of the QuerySet that will be evaluated. In case the queryset is not specified, it will look for the model attribute, and work with the _default_manager for that model.
If you specified an ordering attribute, than that means it will also order the queryset.
I'm building a simple blog app and trying to use slugs in the URL. I have defined the URL as well as the view and the template, however when I try to access the URL /blog/posts, it throws a 404 that too from an entirely different view. All other URLs work, just having an issue with above mentioned one.
url.py
urlpatterns = [
path('',views.IndexView.as_view(),name='index'),
path('create/',views.PostCreateView.as_view(),name='blog-create'),
path('posts/',views.UserPosts.as_view(),name='user-posts'),
path('<slug:slug>',views.PostDetailView.as_view(),name='blog-detail'),
path('list/',views.PostListView.as_view(),name='blog-list'),
path('<slug:slug>/comment/',views.create_comment,name='comment'),
]
Associated Views in order
class PostDetailView(LoginRequiredMixin,generic.DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
slug_url_kwarg = 'slug'
slug_field = 'slug'
class UserPosts(LoginRequiredMixin,generic.ListView):
model = Post
context_object_name = 'posts'
template_name = 'blog/user_post_list.html'
def get_queryset(self):
posts = super().get_queryset()
queryset = posts.objects.all(user= self.request.user)
return queryset
the 404 error
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/blog/posts
Raised by: blog.views.PostDetailView
No post found found matching the query
template directory tree
blog/
templates/
blog/
user_post_list.html
Also earlier I had a problem with path('/list') as well however it got solved when I removed the '/' from the slug URL, I don't know why it worked, can anyone explain?
I am trying to compare the PK in the URL with the request.user.id so that no one can view other's profile. This might not be the conventional way to do it, but I'd still like to know what is wrong with my code. I'm a new learner so bear with me.
views.py
class UserDetail(DetailView):
queryset = Profile.objects.all()
template_name = 'details.html'
def get_queryset(self):
if self.request.user.id != self.kwargs['pk']:
queryset = Profile.objects.first()
return queryset
else:
return self.queryset
models.py
class Profile(AbstractUser):
type = models.CharField(max_length=50)
urls.py
url(r'^details/(?P<pk>\d+)/$', login_required(views.UserDetail.as_view())),
When I go to the URL:
ERROR
Exception Type: AttributeError
Exception Value: 'Profile' object has no attribute 'filter'
A Profile instance is not a queryset.
You shouldn't be overriding get_queryset, you should be overriding get_object, which returns the specific object you want to display.
My models.py looks like this:
class Person(models.Model):
Name = models.CharField(max_length=100)
class Lecture(models.Model):
Speaker = model.ForeignKey(Person)
Topic = models.CharField(max_length=100)
Choices = ((1,"Upcoming"),(2,"In Progress",),(3,"Completed"))
Status = models.SmallIntegerField(choices=Choices, default=1, max_length=1)
My admin.py looks like this:
class LectureAdmin(admin.ModelAdmin):
def get_queryset(self):
return Lecture.objects.exclude(Status='Completed')
So my change list view in the django admin for the Lecture model shows only Lectures in "Upcoming" and "In Progress" status. This works fine.
Now I need to get the URL for the list of all lectures to be passed as a view somewhere else.The standard way of doing this in the django admin is by reversing the URL, so I do this:
urlresolvers.reverse('admin:%s_%s_changelist' % (app_label, model_name))
However, when I do this,I get the the filtered Queryset with Lectures in "Completed" state missing.How do I construct a url reverse function to get entire Lecture queryset and not the filtered queryset?
Here's a workaround, looks ugly, I understand.
Add all GET parameter to the changelist url:
url = urlresolvers.reverse('admin:%s_%s_changelist' % (app_label, model_name))
url += '?all'
Call get_queryset() on super(), exclude Completed status only if there is no all in request.GET:
class LectureAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = super(LectureAdmin, self).get_queryset(request)
if 'all' not in request.GET:
qs = qs.exclude(Status='Completed')
return qs
UPD (applying other filters from request.GET):
from xadmin.plugin.related import RELATE_PREFIX # or just set RELATE_PREFIX = '_rel_'
qs = qs.filter(**{key[len(RELATE_PREFIX):]: value
for key, value in request.GET.iteritems()
if key.startswith(RELATE_PREFIX)})
** unpacks the dictionary into keyword arguments.
Hope it works for you.
get_queryset() is the basic queryset used in admin listing, thus you wo'nt be able to get all the records if you override it this way.
Possible solutions:
use filters ( https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.filter_vertical ) to exclude unwanted records (these with Status='Completed'
or
create proxy model for Lecture, register it in admin and use modified get_queryset() in given listing. Proxy model is required because each model can have registered only single AdminModel class
models.py
class IncompletedLecture(Lecture):
class Meta:
proxy = True
admin.py
class IncompletedAdmin(admin.ModelAdmin):
def get_queryset():
return Lecture.query.exclude(Status='Completed')
admin.site.register(IncompletedLecture, IncompletedAdmin)