Reputation: 414
I'm trying to return the count of a filtered qweryset in my Django views. How do I achieve this while avoiding the 'The QuerySet value for an exact lookup must be limited to one result using slicing.' error?
I've tried to use len() but I still arrive at the same error.
This is my code:
models
class Post(models.Model):
STATUS_CHOICES = (
('draft', 'Draft'),
('published', 'Published'),
)
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200)
author = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
body = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE,
blank=True, null=True)
read_time = models.CharField(max_length=256, null=True, blank=True)
count_visits = models.IntegerField(default=0)
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES,
default='published')
class Meta:
ordering = ('-publish',)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:blog_post', args=[self.slug])
def get_body_as_markdown(self):
return mark_safe(markdown(self.body, safe_mode='escape'))
class PostView(models.Model):
post = models.ForeignKey(Post, on_delete=models.SET_NULL, null=True,
related_name='postviews')
ip = models.CharField(max_length=50)
http_host = models.CharField(max_length=256, null=True, blank=True)
http_referrer = models.CharField(max_length=256, null=True, blank=True)
http_user_agent = models.CharField(max_length=256, null=True, blank=True)
remote_host = models.CharField(max_length=256, null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.ip
views:
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post.html'
context_object_name = 'post'
def get(self, request, *args, **kwargs):
self.slug = get_object_or_404(Post, slug=self.kwargs['slug'])
p = Post.objects.filter(slug=self.slug)
count_visits = None
unique_views = set()
if self.request.user.is_authenticated:
post_views = PostView.objects.filter(post=p)
count_visits = post_views.count()
for post_view in post_views:
unique_views.add(post_view.ip)
else:
post_view = PostView(ip=request.META.get('REMOTE_ADDR', ''),
http_host=request.META.get('HTTP_HOST', ''),
http_referrer=request.META.get('HTTP_REFERER',''),
http_user_agent=request.META.get('HTTP_USER_AGENT',''),
remote_host=request.META.get('REMOTE_HOST', ''))
post_view.save()
c = {
'count_visits': count_visits,
'unique_views': unique_views,
}
return render(request, self.template_name, c)
template:
<span>
{% if user.is_authenticated %}
<br>
{{ count_visits }} views, {{ unique_views|length }} unique views.
{% endif %}
</span>
I expect the output to show the number of views in the template, but I'm getting the "The QuerySet value for an exact lookup must be limited to one result using slicing." error.
Many thanks!
Upvotes: 1
Views: 2625
Reputation: 2279
Change this
p = Post.objects.filter(slug=self.slug)
To this
p = Post.objects.filter(slug=self.slug)[0]
ERROR :
filter
returns a queryset, even if there's only one element matching.
PostView.objects.filter(post=p)
fails because it expects post to be an object not a queryset.
UPDATE :
self.slug
will not be initialized because instance variables can only be declared inside __init__
.
You can just delete the self
part to markslug
as a local variable instead
slug = get_object_or_404(Post, slug=self.kwargs['slug'])
p = Post.objects.filter(slug=slug)
Upvotes: 2