I have a project called social clone project in this project you can only post if you are in a group. What i want to happen is if the user created a group the user should automatically in the group, in this case if i created a group i still need to join the group to join my own group. I tried to search this on google but i get no result and i hope someone will help me and btw i'm just new here in programming and i'm currently learning django.
models.py
##########################
## GROUPS VIEWS.PY FILE ##
##########################
from django.contrib.auth import get_user_model
from django import template
from django.db import models
from django.utils.text import slugify
from misaka import html
from django.urls import reverse
User = get_user_model()
register = template.Library()
class Group(models.Model):
name = models.CharField(max_length=255, unique=True)
slug = models.SlugField(allow_unicode=True, unique=True)
description = models.TextField(blank=True, default="")
description_html = models.TextField(editable=False, blank=True, default="")
members = models.ManyToManyField(User, through='GroupMember')
def __str__(self):
return self.name
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
self.description_html = html(self.description)
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse('groups:single', kwargs={'slug': self.slug})
class Meta:
ordering = ['name']
class GroupMember(models.Model):
user = models.ForeignKey(User, related_name='user_groups', on_delete=models.CASCADE)
group = models.ForeignKey(Group, related_name='memberships', on_delete=models.CASCADE)
def __str__(self):
return self.user.username
class Meta:
unique_together = ('user', 'group')
views.py
##########################
## GROUPS VIEWS.PY FILE ##
##########################
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views import generic
from groups.models import Group, GroupMember
from django.urls import reverse
from django.shortcuts import get_object_or_404
from django.db import IntegrityError
from django.contrib import messages
class CreateGroup(LoginRequiredMixin, generic.CreateView):
model = Group
fields = ('name', 'description')
class SingleGroup(generic.DetailView):
model = Group
class ListGroups(generic.ListView):
model = Group
class JoinGroup(LoginRequiredMixin, generic.RedirectView):
def get_redirect_url(self, *args, **kwargs):
return reverse('groups:single', kwargs={'slug': self.kwargs.get('slug')})
def get(self, request, *args, **kwargs):
group = get_object_or_404(Group, slug=self.kwargs.get('slug'))
try:
GroupMember.objects.create(user=self.request.user, group=group)
except IntegrityError:
messages.warning(self.request, (f"Warning, already a group member of {group.name} group."))
else:
messages.success(self.request, (f"You are now a member of {group.name} Group."))
return super().get(request, *args, **kwargs)
class LeaveGroup(LoginRequiredMixin, generic.RedirectView):
def get_redirect_url(self, *args, **kwargs):
return reverse('groups:single', kwargs={'slug': self.kwargs.get('slug')})
def get(self, request, *args, **kwargs):
try:
membership = GroupMember.objects.filter(
user=self.request.user,
group__slug=self.kwargs.get('slug')
).get()
except GroupMember.DoesNotExist:
messages.warning(self.request, ("You can't leave this group because you aren't in it!"))
else:
membership.delete()
messages.success(self.request, ("You have successfully left this group."))
return super().get(request, *args, **kwargs)
group_form.html
{% extends 'groups/group_base.html' %}
{% load bootstrap4 %}
{% block group_content %}
<div class="container">
<h4>Create New Group</h4>
<form id="groupForm" action="{% url 'groups:create' %}" method="POST">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-large btn-primary">Create</button>
{% endbuttons %}
</form>
</div>
{% endblock %}
group_detail.html
{% extends 'groups/group_base.html' %}
{% block pregroup %}
<div class="container">
<h1>{{ group.name }}</h1>
<h2>Member Count: {{ group.members.count }}</h2>
<div class="content">
{% if user in group.members.all %}
<a class="btn btn-lg btn-warning" href="{% url 'groups:leave' slug=group.slug %}"><span class="fa fa-times-circle-o"></span>Leave</a>
{% else %}
<a class="btn btn-lg btn-warning" href="{% url 'groups:join' slug=group.slug %}"><span class="fa fa-check-circle-o"></span>Join</a>
{% endif %}
</div>
</div>
{% endblock %}
{% block group_content %}
<div class="col-md-8">
{% if group.posts.count == 0 %}
<h2>No Post In This Group Yet!</h2>
{% else %}
{% for post in group.posts.all %}
{% include 'posts/_post.html' %}
{% endfor %}
{% endif %}
</div>
{% endblock %}
Override the form_valid method on your CreateGroup view, here you can perform some additional logic after the object has been saved
class CreateGroup(LoginRequiredMixin, generic.CreateView):
model = Group
fields = ('name', 'description')
def form_valid(self, form):
result = super().form_valid(form)
GroupMember.objects.create(user=self.request.user, group=self.object)
return result
ccbv.co.uk is fantastic for inspecting the available methods there are to override for the built-in class based views
Related
in my project, I have three models
models.py
class Category(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField(max_length=50, unique=True, null=True, blank=True)
parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True, related_name='children')
class Tag(models.Model):
tag = models.CharField(max_length=75, verbose_name='Tag')
slug = models.SlugField(null=True)
class Post(models.Model):
title = models.CharField(max_length=150)
slug = models.SlugField(max_length=150, null=True, blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
tags = models.ManyToManyField(Tag, related_name='tags', blank=True)
and I created search filter view in views.py
def is_valid_queryparam(param):
return param != '' and param is not None
class SearchPepsiView(ListView):
template_name = "blog/NewSearch.html"
model = Post
paginate_by = 10
def get_queryset(self):
return Post.objects.filter(category__slug=self.kwargs['slug'])
def get_context_data(self, *args, **kwargs):
context = super(SearchPepsiView, self).get_context_data(*args, **kwargs)
context['category'] = Post.objects.filter(category__slug=self.kwargs['category'])
return context
def get(self, request, *args, **kwargs):
request = self.request
qs = Post.objects.all()
categories = Category.objects.filter(parent=None).order_by('name')
PostOrAuthor_query = request.GET.get('PostOrAuthor')
SearchCategory_query = request.GET.get('SearchCategory')
if is_valid_queryparam(PostOrAuthor_query):
qs = qs.filter(Q(title__icontains=PostOrAuthor_query) |
Q(content__icontains=PostOrAuthor_query) |
Q(author__username__icontains=PostOrAuthor_query)).distinct()
if is_valid_queryparam(SearchCategory_query) and SearchCategory_query != 'Choose...':
qs = qs.filter(category__name=SearchCategory_query)
count = qs.count() or 0
return render(request, self.template_name, {
'queryset': qs,
'categories': categories,
'count': count,
})
and I created Post Per Category View in views.py
class PostPerCategoryCBV(ListView):
model = Post
template_name = 'blog/Category_Projects.html'
def get_queryset(self):
self.category = Category.objects.get(slug=self.kwargs['slug'])
return Post.objects.filter(category=self.category)
def get_context_data(self, **kwargs):
context = super(PostPerCategoryCBV, self).get_context_data(**kwargs)
context['category'] = self.category
return context
and I created Post Per Tag View in views.py
class PostPerTagCBV(ListView):
model = Post
template_name = 'blog/tag_projects.html'
def get_queryset(self):
qs = super().get_queryset()
qs = qs.filter(tags__slug=self.kwargs['slug'])
return qs
and in templates, I created NewSearch.html
<form class="col-12 mt-5 mb-5">
<div class="form-group">
<label for="formGroupExampleInput">Post or Author</label>
<input type="search" name="PostOrAuthor" class="form-control" id="inputAddress" placeholder="Post or Author">
</div>
<div class="form-row">
<div class="form-group col-md-12">
<label for="SearchCategory">Category</label>
<select id="SearchCategory" name="SearchCategory" class="form-control">
<option selected>Choose...</option>
{% for cat in categories %}
<option value="{{ cat.name }}">{{ cat.name }}</option>
{% endfor %}
</select>
</div>
</div>
<button type="submit" class="btn btn-primary col-12">Search</button>
</form>
</div>
<div class="row">
<p>You have<b> {{ count }} </b>search results </p>
</div>
{% if category %}
<div class="row">
{% for post in category %}
{% include 'blog/Post_Loop.html' %}
{% endfor %}
</div>
{% else %}
<div class="row">
{% for post in queryset %}
{% include 'blog/Post_Loop.html' %}
{% endfor %}
</div>
{% endif %}
</div>
and Here is my URLs.py
path('post_list/', PostListCBV.as_view(), name='list-post'),
path("search-pepsi/", SearchPepsiView.as_view(), name="search-pepsi"),
path('tags_projects/<str:slug>/', PostPerTagCBV.as_view(), name='tag-posts'),
path('category_posts/<str:slug>/', SearchPepsiView.as_view(), name='category-posts'),
My success:
When all search fields are empty .. it list all posts.
when entering any search query .. it backs with the search result.
my problem is:
I believe that there are someway I can combine ListView of Post_Per_Category and Post_Per_Tag with Search View (in ONE View and one template) .. so I created def get_queryset(self): to use it but it didn't worked.
Anybody can help please ...
Thanks in-advance
I am trying to add a comment section to add a comment section to my blog detail using django but when i run my server i get no error in the development server and the comments are not being displayed. I added the comments from my admin site.
The snippet of my code is below.
views.py
from .models import Post
from django.utils import timezone
from .forms import PostForm, CommentsForm
from django.contrib.auth.decorators import user_passes_test
# Create your views here.
def home(request):
return render (request, 'blogapp/home.html')
def blog_list(request):
post = Post.objects.order_by('-published_date')
context = {
'posts':post
}
return render(request, 'blogapp/blog_list.html', context)
def blog_detail(request, pk=None):
detail = Post.objects.get(pk=pk)
context = {
'detail': detail
}
return render(request, 'blogapp/blog_detail.html', context)
def add_post(request, pk=None):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid:
body = form.save(commit=False)
body.published_date = timezone.now()
body.save()
return redirect('blog_list')
form = PostForm()
else:
form = PostForm()
context = {
'form': form
}
return render(request, 'blogapp/add_post.html', context)
def add_comments(request, pk=None):
if request.method == "POST":
form = CommentsForm(request.POST)
if form.is_valid:
comment = form.save(commit=False)
comment.date_added = timezone.now()
comment.save()
return redirect('blog_detail')
form = CommentsForm()
else:
form = CommentsForm()
context = {
'form': form
}
return render(request, 'blogapp/add_comments.html', context)
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name="homepage"),
path('blog/', views.blog_list, name="blog_list"),
path('blog/post/<int:pk>/', views.blog_detail, name="blog_detail"),
path('blog/add_post/', views.add_post, name="add_post"),
path('blog/add_comments/', views.add_comments, name="add_comments"),
]
forms.py
from django import forms
from .models import Post, Comments
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('author', 'title', 'post_description', 'image', 'image_description', 'body',)
class CommentsForm(forms.ModelForm):
class Meta:
model = Comments
fields = ('post', 'name', 'body',)
models.py
from django.db import models
from django.utils import timezone
# Create your models here.
class Post(models.Model):
author = models.ForeignKey('auth.user', on_delete=models.CASCADE)
title = models.CharField(max_length=300)
body = models.TextField()
post_description = models.CharField(max_length=500, blank=True, null=True)
image = models.ImageField(blank=True, null=True, upload_to="image/")
image_description = models.CharField(max_length=500, blank=True, null=True)
published_date = models.DateTimeField(default=timezone.now, blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
class Comments(models.Model):
post = models.ForeignKey('Post', related_name="comments", on_delete=models.CASCADE)
body = models.TextField()
name = models.CharField(max_length=300)
date_added = models.DateTimeField(default=timezone.now, blank=True, null=True)
def __str__(self):
return '%s - %s' % (self.post.title, self.name)
blog_detail.html
{% extends 'base.html' %}
{% load static %}
{% block content %}
<article>
<strong>
<h1><b>{{ detail.title }}</b></h1>
</strong>
<h3>POST AUTHOR: {{ detail.author }}</h3>
<h4><i>{{ detail.post_description }}</i></h4>
<h4>PUBLISHED:{{ detail.published_date }}</h4>
<p>
<hr>
{% if detail.image %}
<center>
<br>
<img src="{{ detail.image.url }}" width="1000" height="700">
<br><br>
<i>IMAGE DESCRIPTION: {{ detail.image_description }}</i>
</center>
{% endif %}
<hr>
<br><br><br>
{{ detail.body|linebreaksbr }}
</p>
<hr class="solid">
<h2>COMMENTS ...</h2>Add One
{% for comment in post.comments.all %}
<strong>
{{ comment.name }}-{{ comment.date_added }}
</strong>
{{ comment.body }}
{% endfor %}
</article>
{% endblock %}
add_comments.html
{% extends 'base.html' %}
{% block content %}
<article>
{% if user.is_authenticated %}
<h1>CREATE NEW BLOG POST.</h1>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">ADD COMMENT</button>
<input type="hidden" name="next" value="{% url 'blog_detail' pk=post.pk %}"/>
</form>
{% else %}
<h2>Login HERE to add comments.</h2>
{% endif %}
</article>
{% endblock %}
in your template you use
{% for comment in post.comments.all %}
but in template context there is no post variable
you should use {% for comment in detail.comments.all %}
I have a blog app and the comments can be added on the detail page of the blog, im having trouble implementing a way for users to edit comments they have already made. I have it so a url shows up on comments where only the person signed in can click the link to edit their comments on the post however im getting this error when I try to load the detail page now. Any help would be appreciated thank you
Reverse for 'update_comment' with arguments '(None,)' not found. 1 pattern(s) tried: ['posts(?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)/(?P[-a-zA-Z0-9_]+)/(?P<comment_id>[0-9]+)/update$']
edit:
I corrected my detail.html file and now im getting this error:
update_comment() missing 1 required positional argument: 'id'
models.py
from django.db.models.signals import pre_save
from Date.utils import unique_slug_generator
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
from django.conf import settings
class PublishedManager(models.Manager):
def get_queryset(self):
return super(PublishedManager,
self).get_queryset()\
.filter(status='cleared')
class Post(models.Model):
STATUS_CHOICES = (
('cleared','Cleared'),('UnderReview','Being Reviewed'),('banned','Banned'),)
title = models.CharField(max_length = 300)
slug = models.SlugField(max_length = 300, unique_for_date='publish')
author = models.ForeignKey(User, on_delete=models.SET_NULL, related_name='forum_posts',null=True)
body = models.TextField()
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=12,choices=STATUS_CHOICES,default='cleared')
objects = models.Manager()
cleared = PublishedManager()
class Meta:
ordering =('-publish',)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('posts:post_detail', args=[self.publish.year, self.publish.month, self.publish.day, self.slug])
def slug_generator(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = unique_slug_generator(instance)
pre_save.connect(slug_generator, sender=Post)
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.SET_NULL, related_name='comments',null=True)
name = models.ForeignKey(User, on_delete=models.SET_NULL,null=True)
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
class Meta:
ordering = ('created',)
def __str__(self):
return f'Comment by {self.name} on {self.post}'
urls.py
from . import views
from django.urls import path, include
from django.contrib.auth import views as auth_views
from .views import PostListView, PostCreateView,PostUpdateView
app_name = 'posts'
urlpatterns = [
path('', views.PostListView.as_view(), name='post_list'),
#path('<int:year>/<int:month>/<int:day>/<slug:post>/',views.PostDetailView.as_view(),name='post_detail'),
path('<int:year>/<int:month>/<int:day>/<slug:post>/',views.post_detail,name='post_detail'),
path('post/new/',PostCreateView.as_view(), name='post-create'),
path('<int:year>/<int:month>/<int:day>/<slug:post>/update/',PostUpdateView.as_view(), name='post-update'),
path('<int:year>/<int:month>/<int:day>/<slug:post>/<int:comment_id>/update',views.update_comment, name='update_comment'),
]
views.py
from django.shortcuts import render, get_object_or_404
from .models import Post, Comment
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.views.generic import ListView, CreateView, UpdateView
from .forms import CommentForm, PostForm
from django.contrib.auth.models import User
from django.http import HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect, render
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib import messages
from django.urls import reverse
class PostListView(ListView):
queryset = Post.cleared.all()
context_object_name = 'posts'
paginate_by = 3
template_name = 'posts/post/list.html'
"""class PostDetailView(DetailView, Post):
queryset = Post.cleared.all()
model = Post
fields = ['title','body']
template_name = 'posts/post/detail.html'
form_class = CommentForm
def get_object(self, *args, **kwargs):
return get_object_or_404(
Post,
publish__year=self.kwargs['year'],
publish__month=self.kwargs['month'],
publish__day=self.kwargs['day'],
slug=self.kwargs['post'],
)
return post
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['comment'] = Comment.objects.all()
context['comment_form'] = CommentForm()
return context
def post(self, request, *args, **kwargs):
post = get_object_or_404(Post , slug=post, status='cleared',publish__year=year,publish__month=month,publish__day=day)
comment_form = self.form_class(request.POST)
if comment_form.is_valid():
return HttpResponseRedirect( post.get_absolute_url() )
return render(request, self.template_name, {'comment_form': comment_form})"""
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title','body']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin, UpdateView):
model = Post
fields = ['title','body']
template_name = 'posts/post-update.html'
def get_object(self, *args, **kwargs):
return get_object_or_404(
Post,
publish__year=self.kwargs['year'],
publish__month=self.kwargs['month'],
publish__day=self.kwargs['day'],
slug=self.kwargs['post'],
author=self.request.user
)
def get_success_url(self):
return reverse('posts:post_detail',
args=[
self.object.publish.year,
self.object.publish.month,
self.object.publish.day,
self.object.slug
]
)
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def post_detail(request, year, month, day, post, comment_id = None):
post = get_object_or_404(Post , slug=post, status='cleared',publish__year=year,publish__month=month,publish__day=day)
comments = post.comments.filter(active=True)
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
comment_form.instance.post = post
comment_form.instance.name = request.user
comment_form.save()
return HttpResponseRedirect( post.get_absolute_url() )
else:
comment_form = CommentForm()
return render(request,'posts/post/detail.html', {'post':post , 'comments': comments,'comment_form': comment_form, 'comment_id':comment_id })
def update_comment(request, year, month, day, post,id, comment_id = None):
post = get_object_or_404(Post , slug=post, status='cleared',publish__year=year,publish__month=month,publish__day=day, id=id)
comments = post.comments.filter(active=True)
comment_form = CommentForm
if comment_id:
comment_form = CommentForm(instance=Comment.objects.get(id=comment_id), data=request.POST)
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
comment_form.instance.post = post
comment_form.instance.name = request.user
comment_form.save()
return HttpResponseRedirect( post.get_absolute_url() )
else:
comment_form = CommentForm()
return render(request,'posts/update_comment.html', {'post':post , 'comments': comments,'comment_form': comment_form, 'comment_id':comment_id })
forms.py
from django import forms
from .models import Comment,Post
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('body',)
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields =('title','body',)
detail.html
{% extends "Main/Base.html" %}
{% block title %}{{ post.title }}{% endblock %}
{% block content %}
<h1>{{ post.title }}<h1>
<p class="date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|linebreaks }}
{% if user.is_authenticated and user == post.author %}
<h3>Update your post</h3>
{% endif %}
{% with comments.count as total_comments %}
<h2>
{{ total_comments }} comment{{ total_comments|pluralize }}
</h2>
{% endwith %}
{% for comment in comments %}
<div class="comment">
<p class="info">
Comment by {{comment.name }}
{{ comment.created }}
</p>
{{ comment.body|linebreaks}}
{% if user.is_authenticated and user == comment.name %}
<p>edit</p>
{% endif %}
</div>
{% empty %}
<p>There are no comments yet.</p>
{% endfor %}
{% if new_comment %}
<h2>Your comment has been added.</h2>
{% else %}
{% if request.user.is_authenticated %}
<h2>Add a new comment</h2>
<form method="post">
{{ comment_form.as_p }}
{% csrf_token %}
<p><input type="submit" value="Add comment"></p>
</form>
{% else %}
<h3>You need to be logged in to make a comment please log-in if you dont have an account register here now</h3>
{% endif %}
{% endif %}
{% endblock %}
update_comment.html
{% extends "Main/Base.html" %}
{% block content %}
<form method="POST" action="{% url 'posts:update_comment' post.publish.year post.publish.month post.publish.day post.slug comment.id %}">
{{ comment_form.as_p }}
{% csrf_token %}
<p><input type="submit" value="Add comment"></p>
</form>
{% endblock %}
Ive been trying a bunch of this to try and make it work so my code is a bit of a mess, there aren't many tutorials on making editable comments with django.
How do I get annotate to display data on django template when I load the pastry_list.html page? I have a variable set to the annotate line in views.py. I have a for loop running through the variable in template. Am I missing something or doing something wrong? For some reason it seems that the template is not recognizing the views.py variable. Thanks.
from django.db import models
from django.utils import timezone
from django.urls import reverse
from collections import OrderedDict
class IntegerRangeField(models.IntegerField):
def __init__(self, verbose_name=None, name=None, min_value=None,
max_value=None, **kwargs):
self.min_value, self.max_value = min_value, max_value
models.IntegerField.__init__(self, verbose_name, name, **kwargs)
def formfield(self, **kwargs):
defaults = {'min_value': self.min_value,
'max_value':self.max_value}
defaults.update(kwargs)
return super(IntegerRangeField, self).formfield(**defaults)
class Pastry(models.Model):
CHOICES_DICT = OrderedDict((
("Muffin", "Muffin"),
("Cupcake", "Cupcake"),
("Brownie", "Brownie"),
("Cake", "Cake"),
("Fruit Tart", "Fruit Tart")
))
pastry = models.CharField(max_length=20, default="muffin",
choices=CHOICES_DICT.items())
vote = IntegerRangeField(default=1, min_value=1, max_value=1)
def get_absolute_url(self):
return reverse("pastry_detail", kwargs={'pk': self.pk})
def __str__(self):
return self.pastry
VIEWS.PY
from django.shortcuts import render
from django.views.generic import (TemplateView,ListView,
DetailView,CreateView,
UpdateView,DeleteView)
from django.urls import reverse_lazy
from myapp.models import Pastry
from myapp.forms import PastryForm
from django.db.models import F
ps = Pastry.objects.values('pastry').annotate(total=Count('pastry'))
class PollsListView(ListView):
model = Pastry
def get_queryset(self):
return Pastry.objects.all()
class PollsDetailView(DetailView):
model = Pastry
class PollsCreateView(CreateView):
success_url = reverse_lazy('pastry_list')
form_class = PastryForm
model = Pastry
class PollsUpdateView(UpdateView):
success_url = reverse_lazy('pastry_list')
form_class = PastryForm
model = Pastry
class PollsDeleteView(DeleteView):
model = Pastry
success_url = reverse_lazy('pastry_list')
pastry_list.html (template)
{% extends "base.html" %}
{% block content %}
<div class="jumbotron">
New Poll
<h1>Voting for the favorite pastry</h1>
{% for p in ps %}
{% for k, v in p.items %}
{{v}}
{% endfor %}
{% endfor %}
{% for pastry in pastry_list %}
<div class="pastry">
<h3><a href="{% url 'pastry_detail' pk=pastry.pk %}">
{{ pastry.pastry }}</a></h3>
</div>
{% endfor %}
</div>
{% endblock %}
FORMS.PY
from django import forms
from myapp.models import Pastry
from django.forms import ModelForm
class PastryForm(ModelForm):
class Meta():
model = Pastry
fields = ['pastry', 'vote']
PASTRY_FORM.HTML
{% extends "base.html" %}
{% load widget_tweaks %}
{% block content %}
<div class="jumbotron">
<h1>New Poll</h1>
<form class="post-form" method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
</div>
{% endblock %}
</body>
</html>
I'm working on a school project. Right now any user can ask a question.
In order to notify all the users when any users asks a question I've created a new app & notifying them through the simple 'view' whenever a question is asked. But it's just plain notifications yet.
How can I mark them read once a user opens the Notification tab? Just like on social networks!
I suggest you to use ContentType to make a dynamic notifications fo any models. This snippet below is an example how to implement the notification system;
1. in your models.py
from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
from django.utils.translation import ugettext_lazy as _
class ContentTypeToGetModel(object):
"""
requires fields:
- content_type: FK(ContentType)
- object_id: PositiveIntegerField()
"""
def get_related_object(self):
"""
return the related object of content_type.
eg: <Question: Holisticly grow synergistic best practices>
"""
# This should return an error: MultipleObjectsReturned
# return self.content_type.get_object_for_this_type()
# So, i handle it with this one:
model_class = self.content_type.model_class()
return model_class.objects.get(id=self.object_id)
#property
def _model_name(self):
"""
return lowercase of model name.
eg: `question`, `answer`
"""
return self.get_related_object()._meta.model_name
class Notification(models.Model, ContentTypeToGetModel):
# sender = models.ForeignKey(
# User, related_name='notification_sender')
receiver = models.ForeignKey(
User, related_name='notification_receiver')
content_type = models.ForeignKey(
ContentType, related_name='notifications', on_delete=models.CASCADE)
object_id = models.PositiveIntegerField(_('Object id'))
content_object = GenericForeignKey('content_type', 'object_id')
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
STATUS_CHOICES = (
('reply', _('a reply')),
('comment', _('a comment')),
('message', _('a message'))
)
status = models.CharField(
_('Status'), max_length=20,
choices=STATUS_CHOICES, default='comment')
is_read = models.BooleanField(
_('Is read?'), default=False)
def __str__(self):
title = _('%(receiver)s have a %(status)s in the %(model)s:%(id)s')
return title % {'receiver': self.receiver.username, 'status': self.status,
'model': self._model_name, 'id': self.object_id}
class Meta:
verbose_name_plural = _('notifications')
ordering = ['-created']
2. in your views.py
from django.views.generic import (ListView, DetailView)
from yourapp.models import Notification
class NotificationListView(ListView):
model = Notification
context_object_name = 'notifications'
paginate_by = 10
template_name = 'yourapp/notifications.html'
def get_queryset(self):
notifications = self.model.objects.filter(receiver=self.request.user)
# mark as reads if `user` is visit on this page.
notifications.update(is_read=True)
return notifications
3. in your yourapp/notifications.html
{% extends "base.html" %}
{% for notif in notifications %}
{{ notif }}
{# for specific is like below #}
{# `specific_model_name` eg: `comment`, `message`, `post` #}
{% if notif._model_name == 'specific_model_name' %}
{# do_stuff #}
{% endif %}
{% endfor %}
So, when I creat that notifications?
eg: when the other user send a comment to receiver on this post.
from django.contrib.contenttypes.models import ContentType
def send_a_comment(request):
if request.method == 'POST':
form = SendCommentForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
#instance.sender = request.user
...
instance.save()
receiver = User.objects.filter(email=instance.email).first()
content_type = ContentType.objects.get(model='comment')
notif = Notification.objects.create(
receiver=receiver,
#sender=request.user,
content_type=content_type,
object_id=instance.id,
status='comment'
)
notif.save()
How about menu? Like this stackoverflow, facebook, instagram, or else?
you can handle it with templatetags, eg:
# yourapp/templatetags/notification_tags.py
from django import template
from yourapp.models import Notification
register = template.Library()
#register.filter
def has_unread_notif(user):
notifications = Notification.objects.filter(receiver=user, is_read=False)
if notifications.exists():
return True
return False
and the navs.html menu:
{% load notification_tags %}
{% if request.user.is_authenticated %}
<ul class="authenticated-menu">
<li>
<a href="/notifications/">
{% if request.user|has_unread_notif %}
<i class="globe red active icon"></i>
{% else %}
<i class="globe icon"></i>
{% endif %}
</a>
</li>
</ul>
{% endif %}
First you need ManyToManyField for user module who is readed a post.
profile.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
subscribed = models.ManyToManyField(User, related_name='subscribed', blank=True)
readed = models.ManyToManyField("blogs.BlogPost", related_name='readed', blank=True)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.user.username)
class Meta:
ordering = ("-created",)
If you have a blog model like this:
class BlogPost(models.Model):
author = models.ForeignKey(Profile, on_delete=models.CASCADE)
post_title = models.CharField("Post Title", max_length=150, unique=True)
post_content = models.TextField("Content")
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.post_title
class Meta:
ordering = ("-created",)
You need to create a button function in blog posts view.
def mark_as_read_button(request):
if request.method == "POST":
my_profile = Profile.objects.get(user=request.user)
post = request.POST.get("post_pk")
obj = BlogPost.objects.get(pk=post)
if obj in my_profile.readed.all():
my_profile.readed.remove(obj)
else:
my_profile.readed.add(obj)
return redirect(request.META.get("HTTP_REFERER"))
return redirect("blogs:subscribed-blogs")
Then you can use is in the template file like this:
{% extends 'base.html' %}
{% block title %}Subscribed Blog Posts{% endblock %}
{% block content %}
{% for post in posts %}
<h5 class="card-title">{{ post.post_title }}</h5>
{% if post in readed %}
<form action="{% url "blogs:mark_as_read" %}" method="POST">
{% csrf_token %}
<input type="hidden" name="post_pk" value={{ post.pk }}>
<button type="submit" class="btn btn-danger btn-sm">Mark as unread</button>
</form>
{% else %}
<form action="{% url "blogs:mark_as_read" %}" method="POST">
{% csrf_token %}
<input type="hidden" name="post_pk" value={{ post.pk }}>
<button type="submit" class="btn btn-success btn-sm">Mark as read</button>
</form>
{% endif %}
<p class="card-text">{{ post.created }}</p>
<p class="card-body">{{ post.post_content }}</p>
<hr>
{% endfor %}
{% endblock %}
Good coding.