Featured post is not adding - python

I am building a BlogApp and I am stuck on an Problem.
What i am trying to do
I am trying to build like Featuring Post system. That If user click on feature this post then post should be add in featured posts.
The Problem
When i click on Feature this Post then it doesn't add the post in featured post page.
What have i tried
I also made a another model for Featuring Post BUT it didn't work so then i again implement in Post Model
views.py
def featured(request,user_id):
post = get_object_or_404(Post,post_owner=user_id)
if request.user.is_authenticated:
if request.method == 'POST':
post.featured.add(post)
context = {'post':post}
return render(request, 'featured_posts.html', context)
models.py
class Post(models.Model):
post_owner = models.ForeignKey(User,default='',null=True,on_delete=models.CASCADE)
post_title = models.CharField(max_length=500,default='')
featured = models.ManyToManyField(User,related_name='featured',blank=True,default='')
urls.py
path('featured/<int:user_id>/',views.featured,name='featured'),
detail_view.html
{{ data.post_title }}
Feature this Post
featured_posts.html
{% for s in post.featured %}
{{ s.post_title }}
{% endfor %}
I don't know what i am doing wrong.
Any help would be appreciated.
Thank You in Advance.

You write post.featured.add(post) which is incorrect as featured is a many to many relationship between user and post. You likely want to write:
post.featured.add(request.user)
OR
request.user.featured.add(post)
Also you write if request.method == 'POST': yet the view is reached by a normal get request (you use an anchor tag). Remove that condition and it will work. (Best would be to change the anchor to a form with a submit button)
Also to display featured posts of a user:
{% for s in request.user.featured.all %}
{{ s.post_title }}
{% endfor %}

Related

Django show same content (of a model) in sidebar of every page (also in different apps)

I have two apps: blog and mysite.
In the project folder, I have a template which includes a sidebar template. This sidebar is shown on every page of the project (index pages, mysite pages, blog pages).
One part of this sidebar should show a list of the latest x blog entries (independent of the page where the user is).
blog/models.py
class Post(models.Model):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
title = models.CharField(max_length=264)
text = RichTextField(config_name='detail_text_field', default='')
created_date = models.DateTimeField(default=timezone.now)
blog/views.py
class LatestBlogEntriesListView(ListView):
model = Post
template_name = 'blog/_latest_blog_entries_list.html'
def get_queryset(self):
return Post.objects.all().order_by('created_date')[-3:]
sidebar.html
<div class="row">
{% include 'blog/_latest_blog_entries_list.html' %}
</div>
_latest_blog_entries_list.html
<h4>Latest Blog Entries</h4>
{% for post in objects %}
{{ post.title }}
{% endfor %}
Unfortunately, this does not work. My sidebar only shows the h4 "Latest Blog Entries", but not the posts. How can I do this?
Any help is highly appreciated!
I found to use a context processor, as described in this post: https://dev.to/harveyhalwin/using-context-processor-in-django-to-create-dynamic-footer-45k4
This allows to access a context variable within all pages.
When using ListView the default object is called object_list. Try changing your code to this:
{% for post in object_list %}
{{ post.title }}
{% endfor %}
You can also change this variable name in the view, so that when you catch it on the template, it's a custom name.
class LatestBlogEntriesListView(ListView):
model = Post
template_name = 'blog/_latest_blog_entries_list.html'
context_object_name = "your_new_object_name"

Django form is not valid but no error is sent

I started to learn Django today, but I am stuck at using forms. I have created two forms: /contact and /blog-new. The form at the Contact page is working fine, but the one at /blog-new is redirecting me to the home page after the submission button is pressed and no information is printed in the terminal nor saved in the database.
Code on Github
I appreciate if someone can explain to me what I did wrong as I cannot figure it out. Thank you!
mysite/blog/forms.py
from django import forms
from .models import BlogPost
class BlogPostModelForm(forms.ModelForm):
class Meta:
model = BlogPost
fields = ['title', 'slug', 'content']
mysite/blog/views.py
from .forms import BlogPostModelForm
def blog_post_create_view(request):
# create objects
# ? use a form
# request.user -> return something
form = BlogPostModelForm(request.POST or None)
if form.is_valid():
print(form.cleaned_data)
form.save()
form = BlogPostModelForm()
template_name = 'form.html'
context = {'form': form}
return render(request, template_name, context)
mysite/blog/models.py
from django.db import models
# Create your models here.
class BlogPost(models.Model):
title = models.TextField()
slug = models.SlugField(unique=True)
content = models.TextField(null=True, blank=True)
mysite/mysite/urls.py
from blog.views import (
blog_post_create_view,
)
urlpatterns = [
..
path('blog-new', blog_post_create_view),
..
]
mysite/templates/form.html
{% extends "base.html" %}
{% block content %}
{% if title %}
<h1>{{ title }}</h1>
{% endif %}
<form method='POST' action='.'> {% csrf_token %}
{{ form.as_p }}
<button type='submit'>Send</button>
</form>
{% endblock %}
You need to point to right url in action attribute of form.
<form action="/blog-new/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
I think it's not necessary in your case but you could also refactor your view to match the docs.
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import SomeForm
def some_view(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = SomeForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = SomeForm()
return render(request, 'template_name.html', {'form': form})
You need to point to right url in action attribute of form.
That was not actually the solution but something that helped me to figure out what was wrong.
It is not necessary to point to /blog-new/ as . for action will point to the same page, but I have tried with /blog-new/ as action URL and I was surprised to see that /blog-new/ page doesn't exist.
The bug was in mysite/mysite/urls.py for missing a /:
path('blog-new', blog_post_create_view),
It is funny (and annoying) how a symbol like / missing from your code will mess up everything and make you spend hours trying to find a solution as simple as that.
Thank you for your time spend to have a look over my code and try to help me!

How to display the children of a wagtail page

My requirement sounds simple, but I am struggling to make it work
In my wagtail project I have a BlogListingPage which has many child pages. I am trying to create a list of the children in a web page. Here is my code:
models.py
class BlogListingPage(Page):
...
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
context['blog_pages'] = BlogDetailPage.objects.live().child_of(self)
return context
class BlogDetailPage(Page):
...
blog_listing = models.ForeignKey(BlogListingPage,
on_delete=models.PROTECT,
blank=True,
null=True,)
views.py
def blog(request):
url = 'blog/blog_listing_page.html'
context = {'data': BlogListingPage.objects.all()}
return render(request, url, context)
blog_listing_page.html
{% block content %}
{% for blog_listing in data %}
{% for post in blog_listing.blogdetailpage %}
{{ post.blog_title }}
{% endfor %}
{% endfor %}
{% endblock content %}
I am lost in the fog of the django/wagtail pages/objects/querysets/all()/specific
Can someone please lead me to clear blue water?
A couple of suggestions regarding your approach:
Having a foreign key from BlogDetailPage to BlogListingPage is not the Wagtail way to organise parent-child relationships. All Page objects already have a tree hierarchy and you should rely on that, otherwise you lose all the built-in tree handling provided by Wagtail.
You should use the parent/subpage rules to specify how page types can be related to each other. In you case it would look something like:
class BlogListingPage(Page):
subpage_types = ['myapp.BlogDetailPage']
class BlogDetailPage(Page):
parent_page_types = ['myapp.BlogListingPage']
The get_context() method on BlogListingPage only runs when Wagtail is serving that specific page in a view. It doesn't run when you're iterating over these pages in your blog view. In order to access the children there, you need to do something like this:
{% for blog_listing in data %}
{% for post in blog_listing.get_children.live %}
{{ post.blog_title }}
{% endfor %}
{% endfor %}
i.e., you need to use the get_children() method on the page to obtain it's children. live() further filters this to exclude unpublished pages. Note that this will only work if the blog posts are children (in the Wagtail page tree sense) of the listing page. specific() isn't important here if you're only using the post URL and title - it is relevant if you want to display fields that are part of your model rather than the base Page model.

List of posts not displaying - django

I have created a post model and would like to view the posts in post_list. While creating the new post, it is redirecting to post_list but not displaying any post.
Also, in my post_form I have rendered the fields manually by using django templates. I couldnt figure out where I have made the mistake. Can someone please help me out. Thanks
models.py
class Post(models.Model):
author = models.ForeignKey(User, on_delete = models.CASCADE)
slug = models.SlugField(unique=True, blank=True, default=uuid.uuid1)
By default the context_object_name is object_list
Either you access your Posts list in template with object_list
{% for post in object_list %}
{{ post }} <!-- with lowercase -->
{% endfor %}
Or you change the context_object_name to post_list, so that way you will be able to access the post list with post_list in template
class PostListView(ListView):
model = Post
context_object_name = 'post_list'
You probably need to use lowercase variable name in your post_list.html.
For instance, {{ Post.title }} should probably be lowercase {{ post.title }}.
There are a few places to change that.

Django - proper way to implement threaded comments

I'm developing a blog site using Django. My site will allow users to comment on any of my blog posts and also reply to each other and will be displayed using a 'threaded comments' structure (I haven't started user functionality yet, just comments). I've got the threaded comments to work properly using django-mptt (at least, for now), but I have NO CLUE if the route or steps I'm taking are in the right direction. Almost all the tutorials I've gone through only scratch the surface when it comes to comments and doesn't talk about threaded comments in django. I want some experienced/professional advice on what I might be doing wrong and what I could be doing better. The last thing I want is to find out there was a much more acceptable way of going about, after hours of work put in.
So, here is a list of what I need clarity on:
django-mptt:
I chose this because I can afford slower write times. My site will have more reads than writes. Is this option okay for my case? Is there a better alternative I don't know about?
What should I do if my site does end up having lots of commenting activity? What could I do to optimize tree restructuring? Or would I be better off switching to an adjacency list?
My MPTT comment model has a ForeignKey referenced to itself (for replies). Is this the right way? Or should I create a separate reply model?
The way I insert a reply to another user's comment in the tree is using a hidden input within the form that's within the mptt recursive template tags, and return the input value (which is the id of the comment that the reply is for) and set the parent of the reply to that input value. Is this an accepted method?
Multiple forms on one HTML page
I have two forms on my blog post HTML page. One to comment on the blog post, and one to reply to a user's comment. Is this accepted? Or should I create different URLs and view functions for different forms? I did it this way because I wanted a Reddit style commenting system. I don't want it to have to go to a different page to comment or reply.
If a user comments on my blog post, the hidden input value within the reply form returns nothing, therefore I get an error when trying to assign it to a variable in the views.py function. I used a try/except block to fix it. Is there a better way around this?
I'm sorry if these are noob questions and for my post being so long. I just want to do things the best way possible using realistic solutions for a beginner. Any feedback would help. Thank you! Here's my code for my blog app.
models.py
from django.db import models
from mptt.models import MPTTModel, TreeForeignKey
class Post(models.Model):
"""Blog post"""
title = models.CharField(max_length=200)
body = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.body[:50] + '...'
class Comment(MPTTModel):
"""User comment"""
post = models.ForeignKey(Post, related_name='comments',on_delete=models.CASCADE)
parent = TreeForeignKey('self', null=True, blank=True, related_name='children',db_index=True, on_delete=models.CASCADE)
user_comment = models.CharField(max_length=500, unique=True)
date_added = models.DateTimeField(auto_now_add=True)
# approved = models.BooleanField(default=False)
class MPTTMeta:
order_insertion_by = ['date_added']
def __str__(self):
return self.user_comment[:20]
'approved' is commented out because I get a 'no such column: approved' error for some weird reason.
forms.py
from django import forms
from .models import Post, Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['user_comment']
views.py
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import reverse
from .models import Post
from .forms import CommentForm
def posts(request):
"""Show all blog posts"""
posts = Post.objects.order_by('-date_added')
context = {
'posts': posts
}
return render(request, 'posts/posts.html', context)
def post(request, post_id):
"""Show single blog post"""
post = Post.objects.get(id=post_id)
comments = post.comments.all()
if request.method != 'POST':
comment_form = CommentForm()
else:
comment_form = CommentForm(data=request.POST)
try:
parent_id = request.POST['comment_id']
except:
pass
if comment_form.is_valid():
comment = comment_form.save(commit=False)
comment.post = post
comment.parent = comments.get(id=parent_id)
comment.save()
return HttpResponseRedirect(reverse('posts:post', args=[post_id]))
context = {
'post': post,
'comment_form': comment_form,
'comments': comments,
}
return render(request, 'posts/post.html', context)
post.html
{% extends 'posts/base.html' %}
{% block blog_content %}
<h1>Post page!</h1>
<h3>{{ post.title }}</h3>
<h4>{{ post.date_added }}</h4>
<p>{{ post.body }}</p>
<form method="post" action="{% url 'posts:post' post.id %}">
{% csrf_token %}
{{ comment_form.as_p }}
<button type="submit">Add comment</button>
</form>
{% load mptt_tags %}
{% recursetree comments %}
<h5>{{ node.date_added }}</h5>
<p>{{ node.user_comment }}</p>
<form method="post" action="{% url 'posts:post' post.id %}">
{% csrf_token %}
{{ comment_form.as_p }}
<input type="hidden" name="comment_id" value="{{ node.id }}">
<button type="submit">Reply</button>
</form>
{% if not node.is_leaf_node %}
<div style="padding-left: 20px">
{{ children }}
</div>
{% endif %}
{% endrecursetree %}
{% endblock %}
urls.py
from django.urls import path
from . import views
app_name = 'posts'
urlpatterns = [
path('posts/', views.posts, name='posts'),
path('posts/<int:post_id>/', views.post, name='post'),
]
MPTT trees are great for getting a list of subnodes or node counts. They are costly for adding/inserting nodes and the cost increases linearly with the size of the three. They are designed to fit tree data into relational databases. Also, don't get fooled by "I'll have much more reads than writes". Ideally, most of the reads should hit a cache, not the database under it.
Why not skip the relational database and go with a NoSQL database that can natively store trees? There are easy integrations for Django and pretty much every NoSQL database you can thing about.

Categories

Resources