Only Show Edit Button On Owner's Posts - Django - django

I have a Django project with posts and the ability to edit posts.
On the main index page, I am showing all posts, like a news feed.
Currently anyone can edit any post, but I want to make it so that only the owner of the post can edit. I'm just not sure how to write the urls.py file since I'm using:
path("", views.index, name="index"),
I would probably need to pass either the post id or the username to this, but I'm not sure how to write it. I tried:
path("index", views.index, name="index"),
path("index/<str:pk>", views.index, name="index"),
path("index/<str:username>", views.index, name="index"),
But I get errors.
views.py
def index(request):
list_of_posts = Post.objects.all().order_by('id').reverse()
paginator = Paginator(list_of_posts, 10)
num = request.GET.get('page', 1)
get_page_num = paginator.get_page(num)
return render(request, "network/index.html", {
"list_of_posts": list_of_posts,
"get_page_num": get_page_num,
})
models.py
class User(AbstractUser):
pass
class Post(models.Model):
text = models.TextField(max_length=500, blank=True,
null=True)
username = models.ForeignKey('User',
on_delete=models.CASCADE, related_name='author',
null=True, blank=True)
timestamp = models.DateTimeField(auto_now_add=True)
like = models.ManyToManyField(
User, blank=True, related_name="liked_user")
def __str__(self):
return self.username.username
html to show edit button. I've tried:
{% if post.username == request.user.username %}
<button class="btn-btn primary" my-id="{{i.id}}" id="ebutton-
{{i.id}}" onclick="edit_form(this)" >Edit</button>
<br><br><br>
{% endif %}
Full html of this page:
{% extends "network/layout.html" %}
{% load static %}
{% block body %}
<br>
<h3> <center> Welcome. Here is your news feed: </center> </h3>
<br>
{% for i in get_page_num %}
<div class='card mb-3' style="max-width: 530px;" id="card-posts">
<div class="row no-gutters">
<div class="col-md-8">
<div class="card-body">
<h5 class="card-title"><a href="{% url 'profile'
username=i.username %}">{{i.username}}</a></h5>
<span id="textinfo-{{i.id}}"
class="post">{{i.text}}</span> <br>
<textarea my-id="{{i.id}}" id="edit-me-{{i.id}}"
style="display:none;" class="form-control
textarea" row="3">{{i.text}}</textarea>
<br>
<p class="card-text">{{i.timestamp}}</p>
<div class="like mt-1">
<img my-id="{{i.id}}" id="is-it-liked-{{i.id}}"
class="like-class"
{% if not request.user in i.like.all %}
clicked="no"
src="https://img.icons8.com/emoji/452/white-heart.png"
{%else%}
clicked="yes"
src="https://img.icons8.com/emoji/452/red-heart.png"
{%endif%}
/> <span id="post-count-{{i.id}}">{{i.like.count}}.
</span>
</div>
<br>
{% if request.user == post.username %}
<button class="btn-btn primary" my-id="{{i.id}}"
id="ebutton-{{i.id}}" onclick="edit_form(this)"
>Edit</button>
<br><br><br>
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
<br><br>
<div class="pagination">
<span class="step-links">
{% if get_page_num.has_previous %}
<a href="?page={{ get_page_num.previous_page_number }}"
class="page-link">Previous</a>
{% else %}
<li class="page-item disabled"><a class="page-
link">Previous</a></li>
{% endif %}
{% if get_page_num.has_next %}
<a href="?page={{ get_page_num.next_page_number }}" class="page-
link">Next</a>
{% else %}
<a class="page-link">Next</a>
{% endif %}
</span>
</div>
{% endblock %}
{% block script %} <script src="{% static
'network/network.js'%}"></script> {% endblock %}
With this way I need to pass username to the url but I cannot, without getting errors.
Overall I'm just looking for advice, on how to make the edit button only appear on posts that the current user is an owner on. So that way no one can edit anyone else's posts.

If you want to show all posts and only allow a post author to edit it, you need to inject all posts and the request user in your template context. Then in your template iterate through the posts and check if the author is equal to the request user before showing the edit button. But first you need a foreign key in your post model that refers to the author.
Urls.py
urlpatterns = [path("index/", views.index, name="index")]
Views
def index(request):
list_of_posts = Post.objects.order_by('-id')
request_user = request.user
return render(request, "network/index.html", {
"list_of_posts": list_of_posts,
"request_user": request_user
})
Models
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT, related_name='posts')
# Other fields
Template
{% if post.user == request_user %}
<!--Show the edit button-->
{% endif %}
You can also access directly the request object in your template:
{% if post.user == request.user %}
<!--Show the edit button-->
{% endif %}

I needed to add
{% if request.user == i.username %}
because I was looping in the html page, for i in get_page_num:. I did not previously have the i.

{% if request.user == post.author %}
<button>Delete</button>
{% endif %}
This assumes that you have a Post model with an attribute of author

Related

Django primary key url issues

I am makeing a simple blog project. In my project, there is one page where there is a list of all the blog posts. If you click on the title of the post you are taken to the post. I used (?P\d+) I the URL path so the user would be directed to the correct post. However, this did not work. Any help would be much appreciated.
all_posts.html
{% extends "base.html" %}
{% block content %}
<div class="container">
<h1>Blog Posts</h1>
</div>
<div class="container">
{% for blog_post in object_list %}
<table class="table table-striped">
<ul class="list-group">
<li><a class="btn btn-primary" href="{% url 'blog_app:view' pk=blog_post.pk %}">{{ blog_post.post_title }}</a></li>
</ul>
</table>
{% endfor %}
</div>
{% endblock %}
modles.py
from django.db import models
# Create your models here.
class Blog_Post(models.Model):
slug = models.SlugField(max_length=1000, editable=False, null=True)
post_title = models.CharField(max_length=100, editable=True, blank=False, null=True)
blog_content = models.TextField(max_length=10000, blank=False, editable=True, null=True)
files = models.FileField(blank=True, null=True, upload_to=True)
date = models.DateTimeField(blank=False, null=True, auto_now=True, editable=False)
likes = models.IntegerField(blank=True, null=True, editable=False)
urls.py
from django.urls import path
from . import views
app_name = "blog_app"
urlpatterns = [
path('create/', views.create_blog_post.as_view(), name='create'),
path('view/(?P<pk>\d+)/', views.view_blog_post.as_view(), name='view'),
path('all_posts/', views.all_blog_posts.as_view(), name='all'),
path('delete/<slug:slug>', views.delet_blog_post.as_view(), name='delete')
]
path is for converters like slug, str, int, to work with regex you need re_path
you can just redefine it as "view/<int:pk>/"
{% extends "base.html" %}
{% block content %}
<div class="container">
<h1>Blog Posts</h1>
</div>
<div class="container">
{% for blog_post in object_list %}
<table class="table table-striped">
<ul class="list-group">
<li><a class="btn btn-primary" href="{% url 'blog_app:view' pk=blog_post.pk %}">{{ blog_post.post_title }}</a></li>
</ul>
</table>
{% endfor %}
</div>
{% endblock %}
i think the problem is in this line
<li><a class="btn btn-primary" href="{% url 'blog_app:view' pk=blog_post.pk %}">
when you send data in url tag you don't assign it to prametars ,
the right line will be this :
<li><a class="btn btn-primary" href="{% url 'blog_app:view' blog_post.pk %}">

NoReverseMatch in Django 2

I'm kinda new at this, and I believe I have misunderstood some things so I'll try to describe it as best possible.
I have 3 tables(models), Game, Chapter, Thread.
Chapter and Thread are connected with the Game table.
models.py
class Game(models.Model):
title = models.CharField(max_length=100)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.title
class Chapter(models.Model):
title = models.CharField(max_length=80)
content = models.CharField(max_length=10000, null=True)
game = models.ForeignKey(Game, on_delete=models.CASCADE)
def __str__(self):
return self.chapter
class Thread(models.Model):
title = models.CharField(max_length=100)
content = models.CharField(max_length=1000, null=True)
game = models.ForeignKey(Game, on_delete=models.CASCADE)
def __str__(self):
return self.title
views.py
def chapter(request, game_id):
auth = top_menu = True
chapters = Chapter.objects.filter(game=game_id)
return render(request, 'chapters.html', {"chapters": chapters, "auth": auth, "top_menu": top_menu})
def thread(request, game_id):
auth = top_menu =True
threads = Thread.objects.filter(game=game_id)
return render(request, 'threads.html', {"auth": auth, "threads": threads, "top_menu": top_menu})
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('', index, name="index"),
path('signup/', signup, name="signup"),
path('logout/', logout_view, name="logout"),
path('login/', login_view, name="login"),
path('<int:game_id>/chapters/', chapter, name="chapter"),
path('<int:game_id>/threads/', thread, name="thread"),
]
index.html
{% extends 'base.html' %}
{% block content %}
<div class="container">
<div class="wrapper">
<div class="row">
<div class="col-lg-12 text-center" style="margin-bottom:80px;">
<h1>Welcome to Solo Rpg Helper</h1>
</div>
</div>
{% if auth %}
<div class="row">
<div class="col-lg-12 text-center">
<h1>Your games:</h1>
<ul class="list-group list-group-flush">
{% for game in games.all %}
<li class="list-group-item">{{ game.title }}</li>
{% endfor %}
</ul>
</div>
</div>
{% else %}
<div class="row">
<div class="col-lg-12 text-center sign-buttons">
<h3>Sign in Sign up</h3>
</div>
</div>
{% endif %}
</div>
</div>
{% endblock %}
chapter.html
{% extends 'base.html' %}
{% block top_menu %}
<li>Games</li>
<li>Chapters</li>
<li>Threads</li>
{% endblock %}
{% block content %}
<div class="container">
<div class="wrapper">
{% for chapter in chapters.all %}
<div class="row">
<div class="col-lg-6">
<h1>{{ chapter.title }}</h1>
</div>
<div class="col-lg-6">
<p>{{ chapter.content }}</p>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
index.html works fine because I loop the games so I can access game.id.
In the chapter.html I want to use again game.id but I'm not sure how to access it although it is passed in the function chapter in the views.py (I can see it in the terminal).
If I use it like this:
<li>Chapters</li>
it works, but if I use game.id as in index.html:
<li>Chapters</li>
I get the error:
I'm sorry for the long post.
def chapter(request, game_id):
auth = top_menu = True
chapters = Chapter.objects.filter(game=game_id)
return render(request, 'chapters.html', {"chapters": chapters, "auth": auth, "top_menu": top_menu})
You can't use game.id in the chapters.html template at the moment, because the view doesn't include game in the context dictionary.
A typical approach is to use get_object_or_404, to handle the case when no game matches game_id.
from django.shortcuts import get_object_or_404
def chapter(request, game_id):
auth = top_menu = True
game = get_object_or_404(Game, pk=game_id)
chapters = Chapter.objects.filter(game=game_id)
return render(request, 'chapters.html', {"chapters": chapters, "auth": auth, "top_menu": top_menu, "game": game})

why i am not getting a followed_by(followers) entry showing up on my page

i am making a twitter like clone(just to learn how things works in django)
so i am basically trying to set up a many_to_many relationship.
i want to add the functionality of showing 'FOLLOWED_BY' and 'FOLLOWING' to a user profile but list of 'FOLLOWED_BY' is not showing on the page please someone help me!
in the models.py i have define two relationship
user = models.OneToOneField(settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL, related_name='profile', null=True,
blank=True)
following = models.ManyToManyField(settings.AUTH_USER_MODEL,
related_name='followed_by', blank=True)
and in the user_detail.html i have the code for how a profile should look like
this is the models.py module:
from django.conf import settings
from django.db import models
# Create your models here.
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL, related_name='profile',
null=True,
blank=True)
following = models.ManyToManyField(settings.AUTH_USER_MODEL,
related_name='followed_by', blank=True)
def __str__(self):
return str(self.following.all().count())
below is the code for user_detail.html file:
{% extends "base.html" %}
{% block content %}
<div class="row">
<div class="col-sm-3 col-xs-12" style="background-color: yellow">
<h1>{{ object.username }}</h1>
<p>Followers: {{ object.followed_by.count }}</p>
</div>
<div class="col-sm-9 col-xs-12">
<h1>Tweets</h1>
{% for tweet in object.tweet_set.all %}
{{ tweet }}<br/>
{% endfor %}
<hr/>
<h1>Following</h1>
{% for user in object.profile.following.all %}
<a href='/{{ user.username }}'>{{ user.username }}</a><br/>
{% empty %}
<h4>Not following any users</h4>
{% endfor %}
<hr/>
<h1>Followed By</h1>
{% for profile in object.profile.followed_by.all %}
<a href='/{{ profile.user.username }}'>{{ profile.user.username }}</a><br/>
{% empty %}
<h4>Not followed by any user</h4>
{% endfor %}
</div>
{% endblock content %}
for user profile i am getting the FOLLOWING field as i want but FOLLOWED_BY field is not showing how can i do that (what changes should i do in my code)??
You defined a following field that points to the user model, not to a Profile. As a result a Profile has no followed_by relation, a User object has.
I think it probably is better to let following point to Profile, like:
class UserProfile(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
related_name='profile',
null=True,
blank=True
)
following = models.ManyToManyField(
'self',
related_name='followed_by',
symmetrical=False,
blank=True
)
def __str__(self):
return str(self.following.all().count())
Then you can render this like:
<div class="col-sm-3 col-xs-12" style="background-color: yellow">
<h1>{{ object.username }}</h1>
<p>Followers: {{ object.followed_by.count }}</p>
</div>
<div class="col-sm-9 col-xs-12">
<h1>Tweets</h1>
{% for tweet in object.tweet_set.all %}
{{ tweet }}<br/>
{% endfor %}
<hr/>
<h1>Following</h1>
{% for profile in object.profile.following.all %}
<a href='/{{ profile.user.username }}'>{{ profile.user.username }}</a><br/>
{% empty %}
<h4>Not following any users</h4>
{% endfor %}
<hr/>
<h1>Followed By</h1>
{% for profile in object.profile.followed_by.all %}
<a href='/{{ profile.user.username }}'>{{ profile.user.username }}</a><br/>
{% empty %}
<h4>Not followed by any user</h4>
{% endfor %}
</div>
Your code has however some (serious) anti-patterns. The most important one is that you should not write business logic in the template. You should use the view for that. For example you can specify in the view a context like:
context = {
'tweets': object.tweet_set.all()
'followers': object.profile.following.select_related('user').all()
'followed_by': object.profile.followed_by.select_related('user').all()
}
We here can also use a .select_related() [Django-doc] that will boost performance significantly, since now all the users are fetched in the same query.
You also better use the {% url ... %} template tag [Django-doc] to construct queries. So instead of writing:
<a href="/{{ profile.user.username }}">
it is better to construct the query using a reverse lookup like:
<a href="/{% url 'profile_view' username=profile.user.username %}">

Django-endless-pagination twitter style pagination not working

I am using django-endless-pagination for my project. But its not working as it is supposed to. Neither is it showing the show more or neither does it work when I change the code to onscroll mode.
<script>$.endlessPaginate({paginateOnScroll: true});</script>
However the include tags are working as its showing the snaps in the template. And even both the scripts (i.e. the jquery and endless.js) are there. What am I missing? Your help and guidance will be very much appreciated. Thank you.
models.py:
class SnapGroup(models.Model):
name = models.CharField(max_length=150, blank=True, null=True)
date = models.DateField(default=date.today)
class Snap(models.Model):
date = models.ForeignKey(SnapGroup)
image = models.ImageField(upload_to=get_upload_file_name)
caption = models.CharField(max_length=150, blank=True, null=True)
views.py:
#page_template('snapgroups.html') # just add this decorator
def snaps(
request, template='snaps.html', extra_context=None):
context = {
'snapgroup': SnapGroup.objects.all(),
}
if extra_context is not None:
context.update(extra_context)
return render_to_response(
template, context, context_instance=RequestContext(request))
snaps.html:
{% if snapgroup.count > 0 %}
<div class="endless_page_template">
{% include page_template %}
</div>
{% block js %}
{{ block.super }}
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="{{ STATIC_URL }}endless_pagination/js/endless-pagination.js"></script>
<script>$.endlessPaginate();</script>
{% endblock %}
{% else %}
<li><p>No SNAPGROUP yet!</p></li>
<span class="clear_both"></span>
{% endif %}
snapgroups.html:
{% load endless %}
{% paginate snapgroup %}
{% for sg in snapgroup %}
<h4 id="combination" class="snap_date">{{sg.date|date:'l'}}, {{sg.date}}</h4>
<ul>
{% for snap in sg.snap_set.all %}
<li><img src="{{MEDIA_URL}}{{snap.image}}" alt="{{snap.caption}}" /></li>
{% endfor %}
<span class="clear_both"></span>
</ul>
{% endfor %}
{% show_more %}
Solved my problem by this post. Hope this will help someone else.

Display Profile Picture of Users

I am trying to display the profile pictures of new users registered to a website to show to every user logged onto the website and so far i have been able to display their username but i am having difficulty displaying their profile picture stored in an extended model User profile. How do i make this happen to display it in my templates. A better solution is welcome
Profile_tag
def person(context, e):
"""
Renders a single user object.
"""
to_return = {
'user': context ['user'],
'profile': context['UserProfile.objects.all'],
}
register = template.Library()
register.inclusion_tag('profile/person.html', takes_context=True)(person)
Person.html
<div class="person">
<a href="{% url 'profile_detail' user.username %}">
<span class="username">{{ user.username|slice:"12" }}</span>
<p> <img src="/static/assets/{{profile_picture}}" height="100" width="100"></p>
</a>
</div>
user_list.html
{% load profile_tags %}
{% block main_content %}
<h1>Newest Users</h1>
{% friends_for_user user as friend_dict %}
{% for person in object_list %}
{% dict_entry_for_item person.username from friend_dict as friend %}
{% person person %}
{% ifnotequal person user %}
<form method="POST" action="{% if friend %}{% url 'sg_unfollow' person.username %}{% else %}{% url 'sg_follow' person.username %}{% endif %}">{%csrf_token%}
<input type="submit" value="{% if friend %}Unfollow{% else %}Follow{% endif %}" />
</form>
{% endifnotequal %}
{% endfor %}
Urls.py
urlpatterns = patterns('profile.views',
url(r'^detail/(?P<username>[a-zA-Z0-9_-]+)/$', 'detail',
name='profile_detail'),
)
profile_picture came from where ?
Extended user model Profile user Foreignkey with User then
models.py
class Profile(models.Model):
user = models.ForeignKey(User)
profile_picture = models.ImageField(upload_to='uploads/avatar')
views.py
userDetail = Profile.objects.get(user_id=request.session['_auth_user_id'])
templateVar['profile_picture'] = userDetail.profile_picture