Toluwalemi
Toluwalemi

Reputation: 414

How to fix 'The QuerySet value for an exact lookup must be limited to one result using slicing.' error in Django

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

Answers (1)

Amine Messaoudi
Amine Messaoudi

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

Related Questions