I'm a newbie in Python and I need some help with my code. Not even sure if my title makes sense.
Basically I have my blog and I'm trying to add a sidebar with popular posts. I have created a PostStatistics class to collect the number of visits in each post which can be seen from Django admin.
The PostStatistics class has a ForeignKey to the Post class.
OK, so my problem is in the PostDetail view. I have a QuerySet there called Popular where I retrieve the 5 most popular posts in the last 7 days. There I retrieve the Post_id and Post__Title. I also need to retrieve the Post SLUG but I have no idea how I can do that.
The slug would be used in the following bit of code:
{{ pop_article.post__title }}
The following is what in my models:
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='blog_posts')
updated_on = models.DateTimeField(auto_now=True)
content = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
views = models.PositiveIntegerField(default=0, editable=False)
class Meta:
ordering = ['-created_on']
db_table = "post"
def __str__(self):
return self.title
def get_absolute_url(self):
from django.urls import reverse
return reverse("post_detail", kwargs={"slug": str(self.slug)})
class PostStatistic(models.Model):
class Meta:
db_table = "PostStatistic"
post = models.ForeignKey(Post, on_delete=models.CASCADE)
date = models.DateField('Date', default=timezone.now)
views = models.IntegerField('Views', default=0)
def __str__(self):
return self.post.title
The following is what is in my views:
def PostDetail(request, slug):
template_name = 'post_detail.html'
post = get_object_or_404(Post, slug=slug)
comments = post.comments.filter(active=True)
new_comment = None
context = {}
obj, created = PostStatistic.objects.get_or_create(
defaults={
"post": post,
"date": timezone.now()
},
# At the same time define a fence or statistics object creation
# by two fields: date and a foreign key to the article
date=timezone.now(), post=post
)
obj.views += 1
obj.save(update_fields=['views'])
# Now pick up the list of the last 5 most popular articles of the week
popular = PostStatistic.objects.filter(
# filter records in the last 7 days
date__range=[timezone.now() - timezone.timedelta(7), timezone.now()]
).values(
'post_id', 'post__title'
).annotate(
views=Sum('views')
).order_by(
# sort the records Descending
'-views')[:5] # Take 5 last records
# Comment posted
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
# Create Comment object but don't save to database yet
new_comment = comment_form.save(commit=False)
# Assign the current post to the comment
new_comment.post = post
# Save the comment to the database
new_comment.save()
else:
comment_form = CommentForm()
return render(request, template_name, {'post': post,
'comments': comments,
'new_comment': new_comment,
'comment_form': comment_form,
'popular_list': popular
},)
The following is in my HTML:
<div class="card-body">
{% if popular_list %}
<p class="card-text">
{% for pop_article in popular_list %}
{{ pop_article.post__title }}
<br>
{% endfor %}
</p>
{% endif %}
</div>
Thanks in advance!
you need to add post__slug in values of this query in view function like this
popular = PostStatistic.objects.filter(
# filter records in the last 7 days
date__range=[timezone.now() - timezone.timedelta(7), timezone.now()]
).values(
'post_id','post__slug' ,'post__title'
).annotate(
views=Sum('views')
).order_by(
# sort the records Descending
'-views')[:5]
then you will be able to do like this in the template
{{ pop_article.post__title }}
Related
I just started learning Django. I am building a simple Blog App and I am trying to get the user liked time of post of request.user.
I made a Post model and a Like model. And when user like show the like time of user.
But it is not showing the liked time.
models.py
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=30, default='')
class Likepost(models.Model):
by_user = models.ForeignKey(User, on_delete=models.CASCADE)
post_of = models.ForeignKey(Post, on_delete=models.CASCADE)
date_liked = models.DateTimeField(auto_now_add=True)
views.py
def blog_post_detail(request, post_id):
obj = get_object_or_404(Post, pk=post_id)
accessLikes = obj.likepost_set.all()
for All in accessLikes:
if request.user == All.by_user
All.request.user.date_liked
context = {'obj':obj}
return render(request, 'blog_post_detail.html', context}
What i am trying to do :-
I am trying to access liked time of request.user
It is keep showing :-
Likepost' object has no attribute 'request'
I will really appreciate your help. Thank You
You can obtain the Likepost object of a user by filtering the queryset, and try to retrieve the corresponding like:
def blog_post_detail(request, post_id):
obj = get_object_or_404(Post, pk=post_id)
likepost = obj.likepost_set.filter(by_user=request.user).first()
context = {'obj':obj, 'likepost': likepost}
return render(request, 'blog_post_detail.html', context}
Next you can render this in the template with:
{% if likepost %}
Liked by you at {{ likepost.date_liked }}
{% endif %}
Normally one can prevent multiple Likeposts for the same object and the same user with a UniqueConstraint [Django-doc]:
class Likepost(models.Model):
by_user = models.ForeignKey(User, on_delete=models.CASCADE)
post_of = models.ForeignKey(Post, on_delete=models.CASCADE)
date_liked = models.DateTimeField(auto_now_add=True)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['by_user', 'post_of'],
name='like_once_per_post'
)
]
If you plan to store both like and unlike events, you get the most recent Likemodel with:
def blog_post_detail(request, post_id):
obj = get_object_or_404(Post, pk=post_id)
likepost = obj.likepost_set.filter(by_user=request.user).order_by('-date_liked').first()
# …
I am writing a simple blog app and I'm currently in the position where I need to implement comments on a blog post. So, I have two models:
from django.db import models
from django.shortcuts import reverse
# Create your models here.
class Article(models.Model):
title = models.CharField(max_length=120)
author = models.CharField(max_length=50)
content = models.TextField()
date = models.DateField(auto_now=True)
def get_absolute_url(self):
return reverse('articles:article-detail', kwargs={'id': self.id})
class Comment(models.Model):
author = models.CharField(max_length=50)
content = models.TextField()
date = models.DateField(auto_now=True)
post_id = models.IntegerField()
and a ModelForm:
from django import forms
from .models import Article, Comment
class CommentModelForm(forms.ModelForm):
class Meta:
model = Comment
fields = [
'content',
'author',
]
...when I submit the form, I want my Comment's post_id field to be automatically generated and correspond to my Article's id, i.e. the comment should be located on the page where it was submitted.
Here is my views.py:
def article_detail_view(request, id):
obj = get_object_or_404(Article, id=id)
comments = Comment.objects.filter(post_id=id)
comment_form = CommentModelForm(request.POST or None)
if comment_form.is_valid():
comment_form.save()
comment_form = CommentModelForm()
context = {
'object': obj,
'comments': comments,
'comment_form': comment_form
}
return render(request, 'articles/article_detail.html', context)
Any ideas how can I do that?
I suggest to change the Comment model in order to replace post_id with a foreignkey field. It allows to keep a better link between comments and articles.
class Comment(models.Model):
author = models.CharField(max_length=50)
content = models.TextField()
date = models.DateField(auto_now=True)
post_id = models.ForeignKey(Article, on_delete=models.CASCADE) # cascade will delete the comments if the article is deleted.
Then you only have to change the comment_form validation :
if comment_form.is_valid():
comment = comment_form.save(commit=False)
comment.post_id = obj
comment.save()
comment_form = CommentModelForm()
save(commit=False) allows to create the Comment instance without saving it to database and allow us to specify the post_id with the article instance obj defined above. Then comes the final commit comment.save().
If you prefer to not change your model, you can follow the same logic and replace
comment.post_id = obj by comment.post_id = id.
I have created this application but the problem I face now is one that has kept me up all night. I want users to be able to see and select only their own categories when they want to create a post. This is part of my codes and additional codes would be provided on request
category model
class Category(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1,related_name='categories_created')
name = models.CharField(max_length = 120)
slug = models.SlugField(unique= True)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
post model
class Post(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1,related_name='posts_created') #blank=True, null=True)
title = models.CharField(max_length = 120)
slug = models.SlugField(unique= True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='category_created', null= True)
addition codes would be provided immediately on request. Thanks
View.py in post app
def create(request):
if not request.user.is_authenticated():
messages.error(request, "Kindly confirm Your mail")
#or raise Http404
form = PostForm(request.POST or None, request.FILES or None)
user = request.user
categories = Category.objects.filter(category_created__user=user).distinct()
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user
instance.save()
create_action(request.user, 'Posts', instance)
messages.success(request, "Post created")
return HttpResponseRedirect(instance.get_absolute_url())
context = {
"form": form,
}
template = 'create.html'
return render(request,template,context)
Form
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = [
"title",
"content",
"category",
]
html
{% if form %}
<form method="POST" action="" enctype="multipart/form-data">{% csrf_token %}
{{ form|crispy|safe }}
<input type="submit" name="submit" value="Publish">
</form>
{% endif %}
What you need to do is well-described here. Basically, you are using ModelForm which generates the form from your model. Your model doesn't know anything about filtering by user, so you will need to explicitly add a QuerySet to your form that only shows the desired categories. Change your "categories = ..." line to something like:
form.category.queryset = Category.objects.filter(user=user)
form.fields['category'].queryset = Category.objects.filter(user=user)</strike>
Hello I'm building my own blog and I've been trying to add photos to my blog posts. For now, I can upload photos to the media/documents folder however I'm having problems assigning these photos to posts.
This is how my models.py looks like
class Document(models.Model):
post = models.ForeignKey('blog.Post', related_name='documents', null = True)
description = models.CharField(max_length=255, blank=True)
document = models.FileField(upload_to='documents/')
uploaded_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.text
forms.py
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ('description', 'document')
views.py
#login_required
def model_form_upload(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
form.post = post
form.save()
return redirect('/')
else:
form = DocumentForm()
return render(request, 'blog/model_form_upload.html', { 'form': form })
inside post_detail.html
{% for document in post.documents.all %}
<div style="padding: 10px;">
<p> {{ document.description }} </p>
<img src=""/>
</div>
{% endfor %}
...
{% if user.is_authenticated %}
<a class="btn btn-default" href="{% url 'model_form_upload' pk=post.pk %}">Add photo</a>
{% endif %}
For now, I'm trying to print out the description and then dealing with the urls will be easier. The photos are being uploaded, they just aren't connected to the posts. When I remove the null = True I can't pass makemigrations on the command line. Can anyone help me with what's wrong here?
edit: If it helps, the comments class has the same line of code, but still works just fine:
models.py
class Comment(models.Model):
post = models.ForeignKey('blog.Post', related_name='comments')
author = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return self.text
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('author', 'text')
Your DocumentForm has no post attribute, so it does not know how to save it.
Add to your DocumentForm all fields except post:
class DocumentForm(ModelForm):
class Meta:
model = Document
exclude = ['post']
Then, in the view, do the following:
#login_required
def model_form_upload(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
document = form.save(commit=False)
document.post = post
document.save()
return redirect('/')
else:
form = DocumentForm()
return render(request, 'blog/model_form_upload.html', { 'form': form })
I'm using Pinax to create a new project. For this project I needed to create a new app 'Business' which is much like Profiles but they wanted to keep everything seperate for Businesses.
I'm trying to have the admin be able to change the logo or "avatar" for the business profile. Im using the ImageModel class from Photologue to control the image upload, etc but I ran into a problem. When going through the form, the form goes through and redirects but the image doesn't actually get updated. When you go through the django admin, the image uploads fine.
If someone could take a look and see if something is missing, I've been staring at it for too long, so I need a fresh pair of eyes.
Business Models.py
class Business(models.Model):
name = models.CharField(verbose_name="Name", max_length=140)
desc = models.TextField(verbose_name="Description", null=True, blank=True)
bus_type = models.CharField(verbose_name="Business Type", choices=BUSINESS_TYPES, max_length=20)
location = models.CharField(_("location"), null=True, blank=True, max_length=200)
website = models.URLField(_("website"), null=True, blank=True, verify_exists=False)
created_by = models.ForeignKey(User, related_name="Created By")
admin = models.ManyToManyField(User, related_name="Admin User", null=True, blank=True)
followers = models.ManyToManyField(User, related_name="Followed by", null=True, blank=True)
date_added = models.DateField(verbose_name="Date Added")
class Meta:
verbose_name = "Business"
verbose_name_plural = "Businesses"
def __unicode__(self):
return self.name
class BusinessLogo(ImageModel):
business = models.ForeignKey(Business, related_name="Business Association")
My views.py
#login_required
def changeLogo(request, bus_id):
user = request.user
b = get_object_or_404(Business, pk = bus_id)
if request.method == 'POST':
form = ChangeLogoForm(request.POST, request.FILES, instance = b)
if form.is_valid():
biz_logo = form.save(commit=False)
biz_logo.save()
return HttpResponseRedirect('/')
else:
form = ChangeLogoForm()
return render_to_response('business/changelogo.html',
{'user': user, 'form':form, 'b':b}, context_instance=RequestContext(request))
Forms.py
class ChangeLogoForm(ModelForm):
class Meta:
model = BusinessLogo
def save(self, force_insert=False, force_update=False, commit=True):
f = super(ChangeLogoForm, self).save(commit=False)
if commit:
f.save()
print "form save method was called with commit TRUE"
return f
And finally my changelogo.html
...
{% block body %}
<h1>Change Logo</h1>
<form method="POST" action="" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Upload">
</form>
{% endblock %}
...
Thanks everyone, for taking a look.
Steve
The ChangeLogoForm's model is BusinessLogo, but when calling it's constructor you pass it a Business instance:
b = get_object_or_404(Business, pk = bus_id)
...
form = ChangeLogoForm(request.POST, request.FILES, instance = b)
(And you should probably use a OneToOneField field instead of ForeignKey)