Select random blog post Object from "Post" Model and render In View/Template

239 views
Skip to first unread message

Ronnie Raney

unread,
Jan 6, 2018, 10:49:29 PM1/6/18
to Django users
Greetings! My first time using this forum. Using Django 2.0 for a project. I'm a beginner so I appreciate your patience.

I have a type of blog app,  but I don't want the routing/url to be traditional. I want the user to be able to hit a button that says "Random Post" which takes them to a random blog post.

I'm not at all sure of the best way to approach this, but I'll share what I have for context. This blog works because I have class-based views using ListView and DetailView. I have a template that shows ALL of the blog posts in chronological order as a ListView, and I have a template that shows each blog post as a DetailView. It works great but I want this "random" functionality for my routing.

Model: I created a model called Post with all the typical fields, including a unique autofield. My intention was to randomly select a pk using this autofield. I thought about using a property to do some of the querying/logic for my random functionality, but I'm not sure if this is the best way to do it.

View: I have created a custom method-type view, but it doesn't work.

def random_post(request):
    posts = Post.objects.all()
    shuffle (posts)
    random_obj = posts.first()
    context = {'random_obj': random_obj,}
    return render(request, 'blog/random_post.html', context)

URL: I have a 'path' type urlpattern...

path('posts/random_post/', views.random_post, name='random_post'),

TEMPLATE: Here is the link to my randomly selected blog post...

<a class="btn btn-primary btn-lg" href="{% url 'random_post' %}" role="button">Random Blog Post</a>

The "Detail" template for my blog post has nothing special. The routing seems to work just fine, but the fields are empty. No data is being sent from the model to the view.

FYI, this is actually not a blog. I'm using blog logic for the sake of conversation, but I have a very specialized reason for wanting to choose random objects and render them in a view.

Thanks in advance!

Jani Tiainen

unread,
Jan 7, 2018, 3:29:17 AM1/7/18
to django...@googlegroups.com
Hi.

Since you do have show post url, I would create random view just to redirect to show post view with random post id.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/c68e062a-c2a3-434e-aa70-cfcf3e10e600%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Ronnie Raney

unread,
Jan 7, 2018, 8:32:11 AM1/7/18
to Django users
If I understand you, you suggest using my existing DetailView url which shows the detailed Post, but create a view which selects a random pk.

I thought of this but I don’t know how to go about creeating a view that selects a random pk, then applies to the existing urlpattern.

My guess is to count() the Post objects, then randomly pick one of the objects. Then somehow grab the primary key, post_id, and somehow insert into a urlpattern. Also point to the correct template. Any help appreciated!

m712

unread,
Jan 7, 2018, 8:52:31 AM1/7/18
to django...@googlegroups.com
What you need is actually very simple. Create a 'random_post' URL, which points to a view doing the shuffle logic and returns a redirect to the proper page, say '/post/$id', then make that a valid URL and use a DetailView.
m712
--
https://nextchan.org -- https://gitgud.io/blazechan/blazechan
I am awake between 3AM-8PM UTC, HMU if the site's broken

Jani Tiainen

unread,
Jan 7, 2018, 8:59:39 AM1/7/18
to django...@googlegroups.com
Hi,

Something like this:

post_id = Post.objects.values_list ('id', flat=True).order_by('?').first()
return redirect('postview', id=post_id)


--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

Jason

unread,
Jan 7, 2018, 9:57:45 AM1/7/18
to Django users
Actually, using order_by('?') is the worst thing possible, especially in a largish db in production.

Why?

That query generated is SELECT .... FROM .... WHERE .... ORDER BY RAND() LIMIT N

where the random function is executed for every row in the result set which is ordered by the generated value.  As I'm sure you can imagine, running this on a medium sized db table will be _slow_

Ronnie Raney

unread,
Jan 7, 2018, 11:45:33 AM1/7/18
to Django users
I am familiar with this from searching all over for a solution. The accepted solution is to create a manager.

from django.db.models.aggregates import Count
from random import randint
 
class PaintingManager(models.Manager):
    def random(self):
        count = self.aggregate(count=Count('id'))['count']
        random_index = randint(0, count - 1)
        return self.all()[random_index]

If I use this as a template, it might look like this

class PostManager(models.Manager):
    def random(self):
        count = self.aggregate(count=Count('post_id'))['count']
        random_index = randint(0, count - 1)
        return self.all()[random_index]

I tried this earlier in the week and I got nothing but errors, so I was not executing it properly. I think it told me there was no "aggregates".

Anyway, moving forward... If this worked to draw a random post_id, then how do I get from this model to my DetailView? Remember, currently I am using a simple class-based view - DetailView - to show blog posts. Would I create another DetailView using PostManager as the model, then create another urlpattern?

I really appreciate all of the help with this. It would be nice to get a consensus on how to approach this.

To m712: Your solution might be simple, but I'm not seeing it because there is no code. Your suggestion was to create a random_post URL pointing a view that:
1. does shuffle logic
2. returns a redirect to the proper page, ex. /post/$id (is the $ outdated in Django 2.0?)

Then you say to make a valid URL using a DetailView. Are you saying to make a different urlpattern than the random_post URL, or are you talking about making the random_post URL valid?

Since I'm pretty new to Django, I was confused by your suggestion.


Matemática A3K

unread,
Jan 7, 2018, 2:45:21 PM1/7/18
to django...@googlegroups.com

def random_post(request):
    posts_ids = Post.objects.all().values_list("id", flat=True)
    random_obj = Post.objects.get(id=random.choice(posts_ids))

Ronnie Raney

unread,
Jan 7, 2018, 5:44:35 PM1/7/18
to Django users
Is “”id”” actually post_id in my case?
This is a view right? not part of the model?

Matemática A3K

unread,
Jan 7, 2018, 11:26:44 PM1/7/18
to django...@googlegroups.com
On Sun, Jan 7, 2018 at 7:44 PM, Ronnie Raney <thero...@gmail.com> wrote:

def random_post(request):
    posts_ids = Post.objects.all().values_list("id", flat=True)
    random_obj = Post.objects.get(id=random.choice(posts_ids))
    context = {'random_obj': random_obj,}
    return render(request, 'blog/random_post.html', context)

Is “”id”” actually post_id in my case?

Model: I created a model called Post with all the typical fields, including a unique autofield. My intention was to randomly select a pk using this autofield. I thought about using a property to do some of the querying/logic for my random functionality, but I'm not sure if this is the best way to do it.
 
It should be your pk, if you did it the "standard way" should be "id", the idea is to sample one from all your existing pks - whatever it is.

This is a view right? not part of the model?

Yup
 

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

Ronnie Raney

unread,
Jan 8, 2018, 9:30:49 AM1/8/18
to Django users
Thanks Mat-A3K

Here's what I have so far...

models.py
class Post(models.Model):
    post_id = models.AutoField(primary_key=True)
    .... all the other fields...

views.py

#Normal (not random) post detail view
class PostDetailView(DetailView):
    model = Post
    template_name = 'blog/post_detail.html'

# Random view
def random_post(request):
    post_ids = Post.objects.all().values_list('post_id', flat=True)
    random_obj = Post.objects.get(id=random.choice(post_ids))
    context = {'random_obj':random_obj,}
    return render(request, 'blog/random_post.html', context)

urls.py

urlpatterns = [
    ....
    path('post/<int:pk>/', views.PostDetailView.as_view(), name='post_detail'), ### I included this to show my normal Post Detail view
    path('post/<int:pk>/', views.random_post, name='random_post'),
    ....
]

The error I'm getting now has to do with template rendering. I am confused about what I should be using and where - id, post_id, pk, etc.

Reverse for 'random_post' with no arguments not found. 1 pattern(s) tried: ['post\\/random\\/(?P<id>[0-9]+)\\/$']

Please help me figure out my urlpattern, and corrections to my view. I think we are very close!

Ronnie Raney

unread,
Jan 8, 2018, 9:50:16 AM1/8/18
to Django users
Also, would something like this work, and would it be a way to create the view?

class RandomDetailView(DetailView):
    model = Post
    def random_post(request):
        post_ids = Post.objects.all().values_list('post_id', flat=True)
        random_obj = Post.objects.get(id=random.choice(post_ids))  ###  Is "id" here supposed to be pk, or post_id? 

Matemática A3K

unread,
Jan 8, 2018, 11:23:54 AM1/8/18
to django...@googlegroups.com
You have the same url pointing to different views - 'post/<int:pk>/' - in what you pasted - that's a problem, in the best case only one will be used, don't know which. On the other hand, the error is complaining that what it could resolve has a non-optional id parameter - /post/random/<int:id>/ - which you are not passing (probably a "{% url 'random_post' %}"). Remove the <id> from the pattern or add it to the url template tag if you need it. And you probably need to clean up and fix your urls.py... :)
 

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

Matemática A3K

unread,
Jan 8, 2018, 11:34:34 AM1/8/18
to django...@googlegroups.com
On Mon, Jan 8, 2018 at 11:50 AM, Ronnie Raney <thero...@gmail.com> wrote:
Also, would something like this work, and would it be a way to create the view?

class RandomDetailView(DetailView):
    model = Post
    def random_post(request):
        post_ids = Post.objects.all().values_list('post_id', flat=True)
        random_obj = Post.objects.get(post_id=random.choice(post_ids))  ###  Is "id" here supposed to be pk, or post_id? 
        context = {'random_obj':random_obj,}
        return render(request, 'blog/random_post.html', context)


CBV have a standarized way of getting their object, "get_object()", you have to override that method (or override the "get_queryset()") and make it return your random post (which you already know). But also, you may need to hack more the DetailView because it expects an id - and in the case of random you won't have it, you are choosing the id at random.

Using a "lower level" CBV, like TemplateView may be easier, but if you can't read https://docs.djangoproject.com/en/2.0/topics/class-based-views/intro/ because of the hurry, a FBV will work and it's not a bad option :)
 


--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
Message has been deleted

Ronnie Raney

unread,
Jan 8, 2018, 11:41:28 AM1/8/18
to Django users
Please give me code. Sorry, I can't decipher plain language in forums very well, when talking about code.

My urlpattern:   

 path('post/<int:pk>/', views.PostDetailView.as_view(), name='post_detail'),
 path('post/random/???', views.random_post, name='random_post'),  ### What do I put here?

My view:

def random_post(request):
        post_ids = Post.objects.all().values_list('post_id', flat=True)   ### Is this correct? My field name is post_id - the primary key.
        random_obj = Post.objects.get(pk=random.choice(post_ids))   ### What do I put here? pk, id, post_id?

Matemática A3K

unread,
Jan 8, 2018, 11:48:10 AM1/8/18
to django...@googlegroups.com


On Mon, Jan 8, 2018 at 1:37 PM, Ronnie Raney <thero...@gmail.com> wrote:
Ugh. Sorry for the confusion. Here again are the pieces where I need suggestions. Please suggest actual code. I can't decipher things written in plain language on forums. Code is key for me.

OK, first, calm down, seems that your feelings aren't letting you think and you are "running in circles". Code is a language, just as English, you can understand both, you just need to read it slowly and calmed.
 

    path('post/<int:pk>/', views.PostDetailView.as_view(), name='post_detail'),
 
    path('post/random/???', views.RandomDetailView.as_view(), name='random_post'),     ### I don't know what to put here

No id and point it to the FBV:
    path('post/random/', views.random_post, name='random_post'),     ### I don't know what to put here

 


Also my view:

    def random_post(request):
        post_ids = Post.objects.all().values_list('post_id', flat=True)     ### Is 'post_id' correct? YES That's my field name that is my primary key
        random_obj = Post.objects.get(post_id=random.choice(list(post_ids)))      ### What do I put here? id? pk? post_id? -> You may have to convert the queryset to a list / evaluate but I'm not sure, you'll have to ty it
        context = {'random_obj':random_obj,}
        return render(request, 'blog/random_post.html', context)

I have tried a bunch of different configurations for my urlpattern. I have tried different variable names in my view. I'm not sure that my data is actually being inserted into my view. I don't know what my urlpattern is supposed to be. I don't know which variables to use in my view, regarding the primary key.

Calm down, yes you do, just re-read the thread slowly and you'll find the answers :)
 

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

Ronnie Raney

unread,
Jan 8, 2018, 12:11:04 PM1/8/18
to Django users
Thanks - for clarification. Yes! good advice on remaining calm. Apologies if I seemed intense.

If I use this urlpattern, it takes me to the correct template.

path('post/random/', views.random_post, name='random_post'),

But there is no data. I really wanted to include the pk in the URL, to see which one it was picking at random. But it appears the data is not being queried correctly, or it's not being sent over as part of the view.

Revised view:

def random_post(request):
        post_ids = Post.objects.all().values_list('post_id', flat=True)
        random_obj = Post.objects.get(post_id=random.choice(list(post_ids)))   I am still unsure if this is the correct variable. 
        context = {'random_obj':random_obj,}
        return render(request, 'blog/random_post.html', context)

Also, the here are my imports. Maybe there's something missing:

from django.shortcuts import render, redirect
from .forms import ContactForm
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect
from django.core.mail import send_mail, BadHeaderError
from django.http import HttpResponse, HttpResponseRedirect
from django.utils import timezone
from .models import Book, Writer, Translator, Post
import random


Matemática A3K

unread,
Jan 8, 2018, 12:30:44 PM1/8/18
to django...@googlegroups.com
On Mon, Jan 8, 2018 at 2:11 PM, Ronnie Raney <thero...@gmail.com> wrote:
Thanks - for clarification. Yes! good advice on remaining calm. Apologies if I seemed intense.

If I use this urlpattern, it takes me to the correct template.

path('post/random/', views.random_post, name='random_post'),

But there is no data. I really wanted to include the pk in the URL, to see which one it was picking at random.

You can't change the of a user from a view unless you do a redirect. You can issue a redirect to /post/<id> where the id is what you picked at random at the "random_post" view
 
But it appears the data is not being queried correctly, or it's not being sent over as part of the view.

Revised view:

def random_post(request):
        post_ids = Post.objects.all().values_list('post_id', flat=True)
        random_obj = Post.objects.get(post_id=random.choice(list(post_ids)))   I am still unsure if this is the correct variable. 
        context = {'random_obj':random_obj,}
        return render(request, 'blog/random_post.html', context)

Is your template for a "random_obj" or for a "post" object? What does it expect? You can insert a "import ipdb; ipdb.set_trace()" after "context" in your view and inspect if it is right - it will probably be OK.
Did you rename the post template to random_post and did not change the variables inside? If so, just use something like {"post": random_obj}.
 

Also, the here are my imports. Maybe there's something missing:

from django.shortcuts import render, redirect
from .forms import ContactForm
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect
from django.core.mail import send_mail, BadHeaderError
from django.http import HttpResponse, HttpResponseRedirect
from django.utils import timezone
from .models import Book, Writer, Translator, Post
import random


--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

matthe...@iss.com

unread,
Jan 8, 2018, 12:47:37 PM1/8/18
to Django users
Check this out for how to redirect:

Check this out for how Django determines the name of primary key fields:

The best advice has already been provided: Get a random post id from your model and then redirect to the DetailView of that post.  You can figure it out.

Ronnie Raney

unread,
Jan 8, 2018, 12:51:58 PM1/8/18
to Django users
You can't change the of a user from a view unless you do a redirect. You can issue a redirect to /post/<id> where the id is what you picked at random at the "random_post" view

I can't change the what of a user from the view? Also, what user? I've tried putting <id> in there and it causes errors. I've also tried <int:id>, <pk>, <int:pk>, <post_id>, and <int:post_id>

Is your template for a "random_obj" or for a "post" object? What does it expect? You can insert a "import ipdb; ipdb.set_trace()" after "context" in your view and inspect if it is right - it will probably be OK.
Did you rename the post template to random_post and did not change the variables inside? If so, just use something like {"post": random_obj}.

The template is asking for the name of the url - "random_post". The template is loading right now at /post/random/ - confirmed by inspecting the page. The template expects to get the random_post view, I would assume. There are no errors telling me otherwise. Once again, the template is named random_post. So what I think you're suggesting is to change the contex to this:

        context = {'random_obj':random_post,}

I also tried switching these:

        context = {'random_post':random_obj,}

'random_obj' is what was established here: random_obj = Post.objects.get(post_id=random.choice(list(post_ids)))
'random_post' is the name of my urlpattern, and the name of my template link

Am I getting anywhere near understanding what you're saying? All of the things that I've tried are leading me to a template with blank fields.

Matemática A3K

unread,
Jan 8, 2018, 1:07:58 PM1/8/18
to django...@googlegroups.com
Listen to Mattew's summary
 

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.

Ronnie Raney

unread,
Jan 8, 2018, 2:59:54 PM1/8/18
to Django users
Here's a view that works, in case someone in the future wants cares about this

    def random_post(request):
        post_count = Post.objects.all().count()  
        random_val = random.randint(0, post_count-1)  
        post_id = Post.objects.values_list('post_id', flat=True)[random_val]   
        return redirect('post_detail', pk=post_id)
Reply all
Reply to author
Forward
0 new messages