I have these models:
class Author(Model):
user = OneToOneField(User, on_delete=CASCADE)
# Fields
class Post(Model):
author = ForeignKey(Author, on_delete=CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
and a few views as below:
def authors(request)
authors = Authors.objects.all()
return render(request, 'authors.html', {'authors': authors})
The authors' view has a corresponding url path as below:
path('authors/', authors, name='authors')
in authors.html I loop over authors and for each one, I have a link that
sends the author primary key to the author url and view:
{% for author in authors%}
<a href="{% url 'author' author_pk=author.pk %}"{{author.user.email}}</a><br><br>
{% endfor %}
Ok; Everybody is able to see the list of authors.
I then have author url path as below:
path('authors/<int:author_pk>/', author, name='author')
path('authors/<int:author_pk>/<int:post_pk>/delete/', author_delete_post, name='author_delete_post')
And I have the author view in which I show the posts each author has published and a button to delete it.
def author(request, author_pk)
author=get_object_or_404(Author, pk=author_pk)
author_posts = Post.objects.filter(author=author)
return render(request, 'author.html', {'author_posts': author_posts}
#login_required
def author_delete_post(request, author_pk, post_pk):
author=get_object_or_404(Author, pk=author_pk)
author_post = Post.objects.get(author=author, pk=post_pk) # I know that author=author is redundent but it makes no problem
author_post.delete()
return redirect(author, author_pk)
This author template:
{% for author_post in author_posts %}
{{author_post.title}}<br>
{% if user.is_authenticated and author.user == user %}
Delete<br><br><br>
{% endif %}
{% endfor %}
I am letting those authors who are logged in and are in their own page to be able to see the delete button. This is something like facebook that a user is able to delete just his/her posts, not other's.
My problem:
Suppose there is an other having pk=1 and is logged in.
Although he/she cannot see delete button when he/she is in this page:
'/authors/2/'
he/she can play with the url and delete a post of another user having pk=2
'authors/2/10/delete/'
How can I solve this problem?
You can use the request.user to check if the object belongs to the logged in user
Also you should not need to add the author_pk. You can get the author with author = get_object_or_404(Author, user=request.user)
#login_required
def author_delete_post(request, post_pk):
author = get_object_or_404(Author, user=request.user)
author_post = Post.objects.get(author=author, pk=post_pk) # I know that author=author is redundent but it makes no problem
# check if the post belongs to the logged in user
if author_post.author.user == request.user:
# delete here
return redirect(author, author.pk)
Related
I apologize in advance if this has already been asked, but I couldn't find any answers that answered the problem I'm having:
I need to do something similar to a For...Else loop in a Django template.
I need to show a button on a template base on an if condition:
If the user has already bought this product, then show button 1
If the user has not bought this product, then show button 2
For each product, I have to go through the user's purchases, and then show one button or the other depending on whether they have already bought the product or not.
A simplified (and wrong) version of the code would be like:
{% for product in products %}
//other code here
{% for purchase in purchases %}
{% if purchase.service_id.id == product.id %}
// show button 1
{% else %}
// show button 2
{% endif %}
{% endfor %}
{% endfor %}
However, this code doesn't work, as it shows both buttons as it goes through the for loop.
I can't do a For...Empty because the user may have other purchases (so the for loop wouldn't be empty), but none of them coincide with this product.
Thanks in advance.
EDIT:
Thanks #JashOFHop for the reply! In the end, I found a way around. I'll share it in case anyone else find themselves in this situation:
For clarity, the models concerned in this case are:
class User(AbstractUser):
pass
class Service(models.Model):
user_id = models.ForeignKey("User", on_delete=models.CASCADE, related_name="user_id_services")
name = models.CharField(max_length=64)
status = models.BooleanField(default=True)
description = models.TextField(max_length=300)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="category_name")
price = models.IntegerField()
slots = models.IntegerField(default=1)
amount = models.IntegerField(default=1)
watchedby = models.ManyToManyField(User, blank=True, related_name="watchedby")
class Purchase(models.Model):
user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user_id_purchases")
service_id = models.ForeignKey(Service, on_delete=models.CASCADE, related_name="service_id_purchases")
amountpaid = models.IntegerField()
The view for this template was:
def explore(request):
# get all the active services from the db
services = Service.objects.filter(status=True).order_by('name')
# get the catogories for the filter option
categories = Category.objects.all().order_by('category')
# get the info of the user
userinfo = User.objects.get(username=request.user)
# get the info of the user's purchases
purchases = Purchase.objects.filter(user_id=userinfo)
# render the template
return render(request, "barter/explore.html", {
"services": services,
"categories": categories,
"userinfo": userinfo,
"purchases": purchases
})
And the template, as explained above, rendered all the services and in each is supposed to check whether this user has already bought said service or not.
Solution:
In the view I've added this and passed it also to the template:
# create a list of the IDs of the services purchased by the user to be able to render the buy/bought button correctly
purchases_list = []
for purchase in purchases:
purchases_list.append(purchase.service_id.id)
Then, the template is:
{% for service in services %}
// other code with infomation of the service here
// Important part:
{% if service.id in purchases_list %}
<button>You already bought this service</button>
{% else %}
<button>Buy now</button>
{% endif %}
{% endfor %}
You can create another models, for example Profile
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
cart = models.MenyToManyField(Product, null=True)
so in views for byu button you can write like this
def buy(request, pk): #byu button
product = Product.objects.get(id=pk)
profile = Profile.objects.filter(user.username=request.user)
profile.cart.add(product.id)
return redirect('some html')
another button has the same
def index(request, pk): # for template
product = Product.objects.get(id=pk)
profile = Profile.objects.filter(user.username=request.user)
return response(request, 'some html', context)
so template
{% for i in product%}
{% for products in profile%}
{% for p in products.cart.all %}
{% if i.id in p.id%}
<p>you bought</p>
{% else %}
<a href='byu'>Buy</a>
{% endif %}
{% endfor%}
{% endfor%}
{% endfor%}
but i think there is another the easiest way to solve your problem
Intro: I have a 3 models user, post, group. User is able to make posts however each post has to belong to a group. I have 400 fixed groups. Users have to choose from the existing 400 groups for their posts. Users cannot add, delete, update group's.
Furthermore:
Users can become a member of groups and when they click on a certain group. They see all the posts in that group.
Users can follow-unfollow other users.
**What I have right now:**When a user signs-in. In his home page he sees. All the posts of the each individual group he is a member of. When all the posts from all the groups are done with. He then sees the posts of all the people he follows one by one
What I want: I want the posts to be arranged by time
Example: If one of the people he follows just wrote a post then that post is first. Chronologically the second post was in one of the groups that he is a member of. That post comes second... You get the idea
Below are my models
class Post(models.Model):
user = models.ForeignKey(User, related_name='posts')
group = models.ForeignKey(Group, related_name='posts')
title = models.CharField(max_length=250, unique=True)
message = models.TextField()
created_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
class Group(models.Model):
name = models.CharField(max_length=250, unique=True)
members = models.ManyToManyField(User, through='GroupMember')
my views.py
class Homepage(TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
context = super(Homepage, self).get_context_data(**kwargs)
posts_of_people_i_follow = []
posts_from_groups_that_i_member = []
if self.request.user.is_authenticated():
my = self.request.user
for user in my.following.all():
posts_of_people_i_follow += user.posts.all()
posts_of_people_i_follow.save()
for group in my.group_set.all():
posts_from_groups_that_i_member += group.posts.all()
posts_from_groups_that_i_member.save()
context['posts_of_people_I_follow_list'] = posts_of_people_i_follow
context['posts_from_groups_that_i_member'] = posts_from_groups_that_i_member
return context
In my Templates I currently have
{% for post in posts_from_groups_that_i_member %}
{{ post.title }}
{{ post.message }}
{% endfor %}
{% for post in posts_of_people_I_follow_list %}
{{ post.title }}
{{ post.message }}
{% endfor %}
Option 2: After breaking my head on this for a while I have been trying a different approach. But this looks really expensive
In the user profile model
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
def get_all_posts(self):
my = self.user
all_posts_for_this_user = []
for user in my.following.all():
all_posts_for_this_user += user.posts.all()
all_posts_for_this_user.save()
for group in my.group_set.all():
all_posts_for_this_user += group.posts.all()
all_posts_for_this_user.save()
return all_posts_for_this_user
Then in my templates
{% for post in user.profile.get_all_posts %}
{{ post.title }}
{{ post.message }}
{% endfor %}
Can anyone think of a better solution. Or is this ok
if I understand it correctly, you need post date in your Post model then you can order posts by Post.objects.ordey_by('post_date')
https://docs.djangoproject.com/en/2.1/ref/models/querysets/#django.db.models.query.QuerySet.order_by
Modify your view class by using an 'order_by()' method on the queryset that fetches your post objects and filters them according to when they were created (The 'created_at' field comes in handy here).
Add this to your 'Homepage' view class:
def get_queryset(self):
queryset_list = Post.objects.order_by('-created_at')
Further reading can be found here:
https://docs.djangoproject.com/en/2.1/ref/models/querysets/
EDIT
I think the template rendering issue deserves its own question?
In your template, try this:
{% for post in posts_from_groups_that_i_member %}
{% if post in posts_of_people_I_follow_list %}
{{ post.title }}
{{ post.message }}
{% endfor %}
{% endfor %}
In terms of performance, it may not be the best option but it should work. Let me know your findings.
I'm using Django 1.8.4 and Django-registration-redux for handling user registration. My problem is:
when a user logged in, i.e. james, I want to show his username in toolbar. But the problem is when I visit another user's profile,i.e. mike, the username in toolbar also changes to mike. Which is absolutely forbidden.
I'm getting logged in user as an object in my views to check if the logged in user is same as user's profile is currently visited.
I'm not sure if I should prevent request.user to change in different contexts or there's a problem in my codes:
urls.py
url(r'^users/(?P<slug>\w+)/$', UserProfileDetailView.as_view(), name="profile"),
views.py
class UserProfileDetailView(DetailView):
model = get_user_model()
slug_field = "username"
template_name = "user_detail.html"
def get_object(self, queryset=None):
user = super(UserProfileDetailView, self).get_object(queryset)
UserProfile.objects.get_or_create(user=user)
return user
base.html
{% if user.is_authenticated %}
Submit Link |
Logout |
<b>{{ user.username }}</b>
{% else %}
Register |
Login
{% endif %}
user_detail.html
{% if object == request.user and request.user.is_authenticated %}
<p><a href='{% url "edit_profile" %}'>Edit My Profile</a></p>
{% endif %}
There are 2 users in your context:
object (or user - DetailView will also return current object on lowercased model name) this is user you're viewing
request.user - this is current logged in user
You've used user.name in toolbar instead of request.user.name. That is causing issues.
In my project, users have a manytomanyfield of users they are following. When they look at the list of users who are following them, I want to show a Follow/Unfollow link depending on if they have that person in their following list. For example, if B is following A, and A is following B, then there should be an Unfollow link next to B's name when A views their following list.
unfortunately, it always says "(Follow)" and never gives me the "(Remove)" link even if I'm logged in as a user that's already following that user.
My userprofile model:
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='user_object')
visible = models.BooleanField(default=True)
bio = models.TextField(null=True, blank=True)
avatar = models.FileField(upload_to=get_upload_file_name, null=True, blank=True)
following = models.ManyToManyField(User, related_name='following_list')
and the code I am trying to use in my template:
{% for f in followers %}
{{f.user.username}}
{% if thisuser.user_object.following.all == f.user %}
(Remove)
{% else %}
(Follow)
{% endif %}
{% endfor %}
In my views, I am sending:
def followers(request, user_id=1):
thisuser = request.user
userlookup = User.objects.get(id=user_id)
following = thisuser
followers = UserProfile.objects.filter(following=thisuser)
args = {'following': following, 'thisuser': thisuser, 'userlookup': userlookup, 'followers': followers}
args.update(csrf(request))
return render_to_response('followers.html', args)
Decorate your objects in your view to make life in your template easier:
from django.shortcuts import render
def followers(request, user_id=1):
user = User.objects.get(pk=user_id)
followers = UserProfile.objects.filter(following=request.user)
args = {}
args['following'] = request.user
args['userlookup'] = User.objects.get(pk=user_id)
args['followers'] = []
for f in followers:
user_status = (f, request.user.user_object.following_list.filter(pk=f.pk).exists())
args['followers'].append(user_status)
return render(request, 'followers.html', args)
The key part is this:
(f, request.user.user_object.following_list.filter(pk=f.pk).exists())
This is a 2-tuple, the first item is the user profile object, and the second item is either True or False if this user is being followed. The exists() queryset method returns True if the query would have returned results. I use this to flag each user object.
I then collect this "decorated" lists of user profile objects in a list, which is sent as the context variable followers to the template.
You should avoid doing complex logic in the templates, and whenever possible use the views to send extra information about your objects. This not only enhances the performance of your application (templates are not optimal for heavy processing and the most difficult part to debug); but also keeps your templates free from any logic - beyond what is required to display things.
Now, in your template:
{% for f,following in followers %}
{{f.user.username}}
{% if following %}
(Remove)
{% else %}
(Follow)
{% endif %}
{% endfor %}
You don't need to pass request.user to the template, instead make sure the request context processor is enabled in your settings:
import django.conf.global_settings as DEFAULT_SETTINGS
TEMPLATE_CONTEXT_PROCESSORS += DEFAULT_SETTINGS.TEMPLATE_CONTEXT_PROCESSORS+(
'django.core.context_processors.request',
)
I'm working on an project that let users create blogs and allow other users to comment on each other blogs.When a user creates a blog , their are certain ability that the blog owner can do such as deleting their own comments and the way I'm deleting comments is via hyperlink passing value id .
Everyone can see each other blogs but I want to only show the deletion hyperlink to the owner of the blog , so only the user who created the blog. How can I do this? via template
My models
class Blog(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100)
created = models.DateTimeField(auto_now_add=True)
description = models.TextField()
def Bloglook(request ,animal_id):
Blog = Blog.objects.get(pk=animal_id)
return render(request,'blog.html',{blog':blog})
my blog.html
{% if blog %}
{{blog.name%}}
{% endif %}
How Can I only show this link to the person who created the blog?
Delete blog
Use RequestContext to pass to template which passes request.user variable to template, which you can use to check for owner of blog.
Change your view to use RequestContext as
def Bloglook(request ,animal_id):
Blog = Blog.objects.get(pk=animal_id)
return render_to_response('blog.html',{blog':blog},
context_instance = RequestContext(request))
Then in template do
{% if blog.owner == request.user %}
Delete blog
{%endif%}
{% if request.user==blog.user %} Delete blog{% endif %}
Edited:
This will also be hidden from unautenticated users. Only if user is the owner then he will see the deletion link
Also, you can continue using render, there is no need to change to render_to_response.