I am pretty new to Django and still learning it, but I have to create something like medium.com. I want to show posts to users according to their interests. I added an interests field with a checkbox in a sign-up form. And of course, I added a category to the Post model. So, how can I show (to logged-in users only) publications that they interested in? Here is my models.py file in posts app
from django.db import models from django.utils import timezone from django.urls import reverse # Create your models here. class Post(models.Model): CATEGORY = ( ('sport', 'Sport'), ('science', 'Science'), ('it', 'IT'), ('art', "Art"), ) title = models.CharField(verbose_name="Title for publication", max_length=50) category = models.CharField(verbose_name="Category", choices=CATEGORY, max_length=50) author = models.ForeignKey("accounts.User", verbose_name="Author:", on_delete=models.CASCADE) body = models.TextField(verbose_name="Body for publication") pub_date = models.DateTimeField(verbose_name="Date:", auto_now_add=True) def was_published_recently(self): return self.pub_date >= timezone.now() - datetime.timedelta(days=1) def get_absolute_url(self): return reverse("post_detail", kwargs={"pk": self.pk}) def __str__(self): return self.title
And here is my vies.py file in posts app
from django.views.generic import ListView, DetailView from django.views.generic.edit import CreateView, UpdateView, DeleteView from .models import Post from django.urls import reverse_lazy class PostListView(ListView): model = Post template_name = "index.html" def get_queryset(self): return Post.objects.order_by('-pub_date') class PostDetailView(DetailView): model = Post template_name = "./posts/post_detail.html" class PostCreateView(CreateView): model = Post template_name = "./posts/new_post.html" fields = '__all__' class PostUpdateView(UpdateView): model = Post template_name = "./posts/update_post.html" fields = ['body', 'title', 'category'] class PostDeleteView(DeleteView): model = Post template_name = "./posts/delete_post.html" success_url = reverse_lazy('index') class SportPostListView(ListView): model = Post template_name = "./posts/sports.html" class ITPostListView(ListView): model = Post template_name = "./posts/it.html" class SciencePostListView(ListView): model = Post template_name = "./posts/ilm-fan.html" class ArtPostListView(ListView): model = Post template_name = "./posts/sanat.html"
Here is my index.html file
{% extends 'base.html' %} {% block content %} {% if object_list %} {% for post in object_list %} <h1>{{ post.title }}</h1> <p>{{ post.pub_date }}</p> <p>{{ post.author }}</p> <p>{{ post.body }}</p> <p>{{ post.category }}</p> {% endfor %} {% else %} <p>No posts are available.</p> {% endif %} {% endblock content %}
This is my forms.py file in accounts app
from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User from django.db import models from django import forms class SignUpForm(UserCreationForm): INTERESTS = ( ('sport', 'Sport'), ('science', 'Science'), ('it', 'IT'), ('art', "Art"), ) username = forms.CharField(max_length=128, required=True) email = models.EmailField(verbose_name='emailingiz', unique=True, default='') first_name = forms.CharField(max_length=128, required=True) last_name = forms.CharField(max_length=128, required=True) interests = forms.MultipleChoiceField(required=True, widget=forms.CheckboxSelectMultiple, choices=INTERESTS) USERNAME_FIELD = 'email' class Meta: model = User fields = ('first_name', 'last_name', 'username', 'email', 'interests', 'password1', 'password2')
And lastly this views.py file in accounts app
from django.urls import reverse_lazy from django.views.generic import CreateView from .forms import SignUpForm class SignUpView(CreateView): form_class = SignUpForm success_url = reverse_lazy('login') template_name = 'signup.html'
I have no idea how to do this, but I know that I have to do something in the views.py file in the posts app, however, I do not know what to do. Will be really grateful if you could help me.
Advertisement
Answer
I would make another model Category
. Since it will be its own model, you will be able to add future categories on the fly if needed, rather than having those hardcoded choices
.
class Category(models.Model): name = models.CharField(default=None, blank=True, null=True)
Then your User
and Post
model will each have a ManytoMany
field related to category. Posts can be tagged as certain categories when they are created, and Users will be able to select a number of categories that they are interested in and have them stored in this field:
class User(AbstractBaseUser): interests = models.ManytoManyField(Category, default=None, blank=True)
class Post(models.Model): categories = models.ManytoManyField(Category, default=None, blank=True)
You can make use of a forms.modelChoiceField
which will enable your user to select a category.
In your PostListView, all you need to do is change the get_queryset
method to filter for posts that the user likes.
class PostListView(ListView): model = Post template_name = "index.html" def get_queryset(self): user_interests = self.request.user.interests return Post.objects.filter( categories__in=user_interests).order_by('pub_date')
Then, you should get the posts that share categories with the user’s interests.