I'm creating an app that allows Users to create personal goals and then allow them to view those goals once created. When the goal is created it goes into my database and is put onto a list page that users are able to view. The problem is that all users are able to view these goals, not just their own. Below is my code showing how I am trying to make the lists for the users eyes only:
Models
class Goal(models.Model):
# values that I am storing in my SQLlite DB
title = models.CharField(max_length=1000)
body = models.TextField()
created_data = models.DateTimeField(auto_now_add=True, auto_now=False)
updated_data = models.DateTimeField(auto_now_add=False, auto_now=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
# user = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
# Where I am trying to authenticate the user
def get_queryset(self, *args, **kwargs):
return Goal.objects.all().filter(owner=self.request.user)
Views
def goal_list(request):
goals = Goal.objects.all().order_by('created_data')
return render(request, 'goals/goal_list.html', { 'goals': goals })
#login_required
def goal_create(request, *args):
if request.method == 'POST':
form = forms.CreateGoal(request.POST, request.FILES)
if form.is_valid():
# saving my form
goal_create = form.save(commit=False)
goal_create.user = request.user
goal_create.save()
return redirect('goals:goal_list')
else:
form = forms.CreateGoal()
return render(request, 'goals/goal_create.html', {'form': form})
html
{% block body%}
<div class="container">
<h1>Goals List</h1>
<div class="goals">
{% for goal in goals %}
<div class="goal">
{% these are the values Im displaying on the webpage. %}
<h2>{{ goal.title }}</h2>
<p>{{ goal.goal.title }}</p>
<p>{{ goal.body }}</p>
<p>{{ goal.created_data }}</p>
{# <p>{{ goal.user }}</p>#}
</div>
{% endfor %}
</div>
{% endblock %}
You can't put your get_queryset method in the model, because you don't have access to the request there.
Use the login_required decorator so that only logged-in users can access the view. Then you can filter the queryset using request.user.
from django.contrib.auth.decorators import login_required
#login_required
def goal_list(request):
goals = Goal.objects.filter(user=request.user).order_by('created_data')
return render(request, 'goals/goal_list.html', {'goals': goals })
Related
I want to separate the open sessions in my site so a user can't delete/modify objects from another account. If someone could explain me how do I do that, for example here with the delete function.
This is my code:
models.py
class User(AbstractUser): ##abstract user model
pass
class Auction(models.Model): ##these are the objects
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
name = models.CharField(max_length=64)
img = models.ImageField(upload_to='listing_image', null=True, default="/static/default_image.jpg")
description = models.TextField(blank=True)
starting_bid = models.IntegerField()
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="auctions", null=True)
views.py
#login_required(login_url="login")
def delete(request, id):
auction = get_object_or_404(Auction, id=id)
if request.method == "POST":
auction.delete()
return redirect ('index')
return render(request, "auctions/delete.html", {
"auction": auction
})
html:
{{auction.user}}
<h1>{{ auction.name }}</h1>
<img src="{{ auction.img.url }}" width="600">
<h5>Starting price: ${{ auction.starting_bid }}</h5>
<form method="POST" enctype="multipart/form-data">
{%csrf_token%}
<button type="submit" class="btn btn-primary">Place a New Bid</button>
{{form.as_p}}
</form>
{%for bid in bids%}
<h3>${{ bid.new_bid}}</h3>
{%empty%}
No offers yet.
{%endfor%}
{{auction.description}}
<h2>Comments</h2>
<ul>
<p>New Comment</p>
{%for comment in comments%}
<li>{{comment.user}} - {{comment.created_on}}
<p><h4>{{comment.body}}</h4></p>
</li>
{%empty%}
<li>No comments yet.</li>
{%endfor%}
</ul>
<p>Edit
Delete</p>
You filter on the user as well:
#login_required(login_url='login')
def delete(request, id):
auction = get_object_or_404(Auction, id=id, user=request.user)
if request.method == 'POST':
auction.delete()
return redirect ('index')
return render(request, 'auctions/delete.html', {
'auction': auction
})
This will return a HTTP 404 error in case the .user of the Auction is not the logged in user, so they will not be able to remove the object.
In the template, you need to check if the user is the logged in one to show the buttons. But even if you somehow show buttons, the users will not be able to trigger the logic behnind it if you filter accordingly:
{% if user == auction.user %}
<p>Edit
Delete</p>
{% endif %}
I've got a project where users can enter topics and have the option to make the topic public or private. I want all public topics to be visible to everyone, and private topics to only be visible by the owner of that topic (Project is from Python Crash Course exercise 20-5).
I'm not getting any errors when I click through my project, but when I create a new topic and select the "public" option, it is still not showing up for public viewing.
I've researched similar problems and followed all the suggestions here
How to make a topic public to all users in django? and here Django - How to make the topics, that you create public? Learning Log Project with no luck.
I'm guessing my queryset is not rendering data correctly? I've read the Django documentation on querysets as well, but nothing I've tried has worked. I'm really new to programming so any help would be appreciated. Thank you!
Here's my models.py:
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Topic(models.Model):
"""A topic the user is learning about"""
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
public = models.BooleanField(default=False)
def __str__(self):
"""Return a string representation of the model"""
return self.text
class Entry(models.Model):
"""Something specific learned about a topic"""
topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'entries'
def __str__(self):
"""Retrun a string representation of the model"""
if len(self.text) < 50:
return f"{self.text}"
else:
return f"{self.text[:50]}..."
views.py:
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.http import Http404
from .models import Topic, Entry
from .forms import TopicForm, EntryForm
# Create your views here.
def index(request):
"""The home page for Learning Log"""
return render(request, 'learning_logs/index.html')
#Checks whether logged in user has access to topic
def check_topic_owner(request, topic):
if topic.owner != request.user:
raise Http404
def topics(request):
"""Shows all topics"""
#Gets public topics
public_topics = Topic.objects.filter(public=True).order_by('date_added')
#Gets private topics
if request.user.is_authenticated:
private_topics = Topic.objects.filter(owner=request.user).order_by('date_added')
topics = public_topics | private_topics
else:
topics = public_topics
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
def topic(request, topic_id):
"""Show a single topic and all its entries"""
topic = get_object_or_404(Topic, id=topic_id)
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html', context)
#login_required
def new_topic(request):
"""Add a new topic"""
if request.method != 'POST':
#No data submitted; create a blank form.
form = TopicForm()
else:
#POST data submitted; process data
form = TopicForm(data=request.POST)
if form.is_valid():
new_topic = form.save(commit=False)
new_topic.owner = request.user
new_topic.save()
return redirect('learning_logs:topics')
#Display a blank or invalid form
context = {'form': form}
return render(request, 'learning_logs/new_topic.html', context)
#login_required
def new_entry(request, topic_id):
"""Add a new entry for a particular topic"""
topic = get_object_or_404(Topic, id=topic_id)
check_topic_owner(request, topic)
if request.method != 'POST':
#No data submitted, create a blank form
form = EntryForm()
else:
#POST data submitted; process data
form = EntryForm(data=request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.topic = topic
if new_entry.topic.owner == request.user:
new_entry.save()
else:
return Http404
return redirect('learning_logs:topic', topic_id=topic_id)
#Display a blank or invalid form
context = {'topic': topic, 'form': form}
return render(request, 'learning_logs/new_entry.html', context)
#login_required
def edit_entry(request, entry_id):
"""Edit an existing entry"""
entry = get_object_or_404(Entry, id=entry_id)
topic = entry.topic
check_topic_owner(request, topic)
if request.method != 'POST':
#Initial request; prefill with the current entry
form = EntryForm(instance=entry)
else:
#POST data submitted; process data
form = EntryForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return redirect('learning_logs:topic', topic_id=topic.id)
context = {'entry': entry, 'topic': topic, 'form': form}
return render(request, 'learning_logs/edit_entry.html', context)
new_topic.html:
{% load bootstrap4 %}
{% block page_header %}
<h3>Add a new topic:</h3>
{% endblock page_header %}
{% block content %}
<form method="post" action="{% url 'learning_logs:new_topic' %}" class="form">
{% csrf_token %}
{% bootstrap_form form %}
<div class="form-check">
<input class="form-check-input" type="checkbox" value=True id="public" />
<label class="form-check-label" for="public">
<p>Make post public?<p/>
</label>
</div>
{% buttons %}
<button name="submit" class="btn btn-primary">Add topic</button>
{% endbuttons %}
</form>
{% endblock content %}
And forms.py:
from .models import Topic, Entry
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['text']
labels = {'text': ''}
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['text']
labels = {'text': ''}
widgets = {'text': forms.Textarea(attrs={'cols': 80})}
And topics.html:
{% block page_header %}
<h1>Topics</h1>
{% endblock page_header %}
{% block content %}
<ul>
{% for topic in topics %}
<li><h4>
{{ topic }}
</h4></li>
{% empty %}
<li><h4>No topics have been added yet.</h4></li>
{% endfor %}
</ul>
<h4>Add a new topic</h4>
{% endblock content %}
Edit:
I've gotten the project to work as expected now with the help from hasnain095. Here is my updated new_topic.html. I still don't understand how the checkbox is being generated since I've deleted the html which I thought was specifically generating it, but since it's still working, I'm content.
{% extends "learning_logs/base.html" %}
{% load bootstrap4 %}
{% block page_header %}
<h3>Add a new topic:</h3>
{% endblock page_header %}
{% block content %}
<form method="post" action="{% url 'learning_logs:new_topic' %}" class="form">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button name="submit" class="btn btn-primary">Add topic</button>
{% endbuttons %}
</form>
{% endblock content %}
I think the issue is that you've only specified the field 'text in your Topic Form:
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['text']
labels = {'text': ''}
but in new_topic.html file, you have only one checkbox input, no place to enter the text
<div class="form-check">
<input class="form-check-input" type="checkbox" value=True id="public" />
<label class="form-check-label" for="public">
<p>Make post public?<p/>
</label>
</div>
{% buttons %}
<button name="submit" class="btn btn-primary">Add topic</button>
{% endbuttons %}
Your checkbox for "Make post public?" is getting mapped to the field "text" of your Topic.
To fix do:
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['text', 'public']
labels = {'text': ''}
and
<div class="form-check">
<input class="form-check-input" type="checkbox" value=True id="public" />
<label class="form-check-label" for="public">
<p>Make post public?<p/>
</label>
<input class="form-input" type="input" value=True id="text1" />
<label class="form-label" for="text1">
<p>Text<p/>
</label>
</div>
Use unions to add to a queryset:
def topics(request):
"""Shows all topics"""
# Gets public topics (renamed to topic_set to not shadow function name)
topic_set = Topic.objects.filter(public=True).order_by('date_added')
# Add private topics, if any. We save some work by not including the public
# topics.
if request.user.is_authenticated:
topic_set = topic_set.union(
Topic.objects.filter(public=False, owner=request.user)
)
context = {'topics': topic_set}
return render(request, 'learning_logs/topics.html', context)
Side note: add indexes to fields you filter on:
class Topic(models.Model):
"""A topic the user is learning about"""
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
public = models.BooleanField(default=False, db_index=True)
def __str__(self):
"""Return a string representation of the model"""
return self.text
It's working for me after use union to append private topics to public topic, but there maybe some different. list below, hope will help:
views.py
def check_topic_owner(topic, request):
if not topic.public:
if topic.owner != request.user:
raise Http404
def topics(request):
#Gets public topics
public_topics = Topic.objects.filter(public=True)
#Gets private topics and append the public topic
if request.user.is_authenticated:
private_topics = Topic.objects.filter(owner=request.user)
topics = public_topics.union(private_topics).order_by('date_added')
else:
topics = public_topics
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
models.py
class Topic(models.Model):
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
public = models.BooleanField(default=False)
def __str__(self):
return self.text
I want a public page showing the profile of a specific user (so I cannot grab the user id from the logged-in users). I'm not able to select their specific compositions.
I am using a custom user model, so that I have a User class and then a Profile class which is linked to the User via OneToOneField (see code below). I also have a Composition class, which is linked to a specific composer via a ForeignKey. I am able to get the details of a specific profile and I'm also able to print out all the compositions (using Composition.objects.all()).
My models.py:
class User(AbstractBaseUser):
email = models.EmailField(unique=True, max_length=255)
full_name = models.CharField(max_length=255, blank=True, null=True)
objects = UserManager()
[...]
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
[...]
def get_absolute_url(self):
return reverse('profile', args=[str(self.id)])
class Composition(models.Model):
title = models.CharField(max_length=120) # max_length = required
description = models.TextField(blank=True, null=True)
composer = models.ForeignKey(
USER, #my custom user model
on_delete=models.CASCADE
)
def get_absolute_url(self):
return reverse("composition-detail", kwargs={"id": self.id})
def __str__(self):
return "%s" % (self.title)
My views.py:
def profile_details(request, id):
compositions = Composition.objects.filter(composer__id=id)
context = {
"object_list": compositions
}
return render(request, "profile.html", context)
My urls.py:
path('profile/<int:id>/', views.profile_details, name='profile')
My template.html:
{% extends "base.html" %}
{% block content %}
<div class="content-section">
<div class="media">
<img class="rounded-circle account-img" src="{{ user.profile.image.url }}">
<div class="media-body">
<h2 class="account-heading">{{ user.full_name }}</h2>
<p>{{ obj.profile_id}}</p>
<p class="text-secondary">{{ user }}</p>
{% for composition in object_list %}
<li>{{ composition.title }}</li>
{% endfor %}
</div>
</div>
</div>
{% endblock content %}
I'm expecting to see the compositions by that specific composer, i.e. user associated to the profile I'm viewing.
Edit
I've made some progress by adding some custom data to my view:
class ProfileDetails(generic.DetailView):
model = Profile
template_name = 'profile.html'
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
# Add in a QuerySet of all the books
context['compositions'] = Composition.objects.filter(id=1)
return context
Now the context contains indeed all the composition. How do I filter only the composition by the user connected to the profile I'm viewing?
After many tests I've apparently finally come to a solution.
My views.py:
def profile_details(request, pk):
compositions = Composition.objects.filter(composer__id=pk)
context = {
"compositions": compositions,
}
return render(request, "profile.html", context)
And my urls.py:
path('profile/<int:pk>/', views.profile_details, name='profile')
The pk, which is the same for User and Profile objects, is taken from the URL, and used for filtering the Composition objects and find only the compositions by that composer.
I am new to Python and Django and now developing a blog.I have this model that users can log in to the site and add their posts and all posts are displayed in home. At the same time I want the posts by the user to be displayed in user profile.
My model for the blogpost is
from django.db import models
from django.utils import timezone
class Blogpost(models.Model):
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
and the views.py:
views.py
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from blogpost.models import Blogpost
def home(request):
context = {}
template = 'home.html'
return render(request,template,context)
def about(request):
context = {}
template = 'about.html'
return render(request,template,context)
#login_required(login_url='/accounts/login/')
def userprofile(request):
user = request.user
context = {'user': user}
user_posts=Blogpost.objects.filter(author=request.user).order_by('-published_date')
template = 'profile.html'
return render(request,template,context,{'user_posts':user_posts})
I am using this template to display the posts from users returned by the query set.
profile.html
{% extends 'base.html' %}
{% load staticfiles %}
{% block blogprofile %}
{{ user.username }}
{{ user.email }}
{% for Blogpost in user_posts %}
<div class="post">
<div class="date">
<p>{{ Blogpost.published_date }}</p>
<p>{{ Blogpost.author }}</p>
</div>
<h1>{{ Blogpost.title }}</h1>
<p>{{ Blogpost.text|linebreaksbr }}</p>
</div>
{% endfor %}
{% endblock %}
Now I am getting only user name and email when I open the profile.html where as the post is not getting retrieved. Can anyone please correct me on where I am making mistake.
Change your view to this:
#login_required(login_url='/accounts/login/')
def userprofile(request):
user = request.user
user_posts = Blogpost.objects.filter(author=request.user).order_by('-published_date')
template = 'profile.html'
return render(request, template, {'user_posts':user_posts,'user': user})
First off, I'm sure this is a simple question. I'm just getting started with Django and as all beginners do, I thought I'd build a simple blog.
I have a simple data model, a Post model that contains with a link to a user via a FK.
models.py
class Post(TimeStampedActivate):
"""
A blog post
"""
title = models.CharField(max_length=255)
slug = models.SlugField()
excerpt = models.TextField(blank=True)
body = models.TextField()
publish_at = models.DateTimeField(default=datetime.datetime.now())
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=False, help_text='Is the blog post active?')
user = models.ForeignKey(User, related_name='post_user')
def __unicode__(self):
return self.title
I then want a page that lists all of the Posts alongside the username of the person who created the post. My current view looks like this:
views.py
def index(request):
posts = Post.objects.filter(active=True)
user = User.objects.get(id=1)
return render(request, 'blog/index.html', {'posts': posts, 'user': user})
My template
At present this just displays the users name that matches an ID of 1.
{% for post in posts %}
<h2>{{ post.title }}</h2>
<p>{{ post.excerpt|truncatewords:30 }}</p>
<p>Posted by {{ user.first_name }} {{ user.last_name }}</p>
{% endfor %}
How would I modify my views.py file to ensure I get the first and last name of the user responsible for the post?
View:
def index(request):
posts = Post.objects.filter(active=True)
return render(request, 'blog/index.html', {'posts': posts})
Template:
{% for post in posts %}
<h2>{{ post.title }}</h2>
<p>{{ post.excerpt|truncatewords:30 }}</p>
<p>Posted by {{ post.user.first_name }} {{ post.user.last_name }}</p>
{% endfor %}
Use "post.user" in your template instead of just "user".
{{ post.user.first_name }} {{ post.user.last_name }}
"user" is the current logged-in user.
If you use RequestContext & added auth to your TEMPLATE_CONTEXT_PROCESSORS, user is equal to request.user. https://docs.djangoproject.com/en/dev/topics/auth/#authentication-data-in-templates