I have made a blogger website on Django and I would have a page where the user can see/manage their own posts, so they can edit, update and delete.
I have tried to add the page but it keeps throwing an error saying no reverse match?
I am sure how to solve this problem, is something to do with how I have added the author in the Post model to PostAuthor?
This is my models file
class PostAuthor(models.Model):
user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True)
bio = models.TextField(max_length=400, help_text="Enter your bio details here.")
class Meta:
ordering = ["user", "bio"]
def get_absolute_url(self):
return reverse('post-by-author', args=[str(self.id)])
def __str__(self):
return self.user.username
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(PostAuthor, on_delete=models.CASCADE, null=True, blank=True)
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)
class Meta:
ordering = ['-created_on']
def get_absolute_url(self):
return reverse('post-detail', args=[str(self.id)])
def __str__(self):
return self.title
URLs file
urlpatterns = [
path('', views.IndexPage.as_view(), name='index'),
path('posts/', views.PostList.as_view(), name='all-posts'),
path('blog/<int:pk>', views.PostDetail.as_view(), name='post-detail'),
path('blog/<int:pk>', views.PostListbyAuthorView.as_view(), name='post-by-author'),
path('accounts/', include('django.contrib.auth.urls')),
]
Views file
class PostListbyAuthorView(generic.ListView):
model = Post
template_name = 'blog/post_list_by_author.html'
def get_queryset(self):
id = self.kwargs['pk']
target_author = get_object_or_404(PostAuthor, pk=id)
return Post.objects.filter(author=target_author)
def get_context_data(self, **kwargs):
context = super(PostListbyAuthorView, self).get_context_data(**kwargs)
context['blog'] = get_object_or_404(PostAuthor, pk=self.kwargs['pk'])
return context
class IndexPage(generic.ListView):
queryset = Post.objects.filter(status=1).order_by('-created_on')
template_name = 'blog/index.html'
class PostList(generic.ListView):
queryset = Post.objects.filter(status=1).order_by('-created_on')
template_name = 'blog/all_posts.html'
class PostDetail(generic.DetailView):
model = Post
template_name = 'blog/post_detail.html'
You are not passing the user's ID on your link, try this:
{% url 'post-by-author' pk=user.id %}
Related
I am trying to get the most out of my Django Blog project and wanted to know how to show in the admin the activities that a user has made like making comments or giving likes to posts.
I need some hints and guidance on how to add this information in the admin.py
Here is the models.py in Blog App
class Post(models.Model):
title = models.CharField(max_length=100, unique=True)
content = RichTextUploadingField(null=True, blank=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='author')
date_posted = models.DateTimeField(default=timezone.now)
slug = models.SlugField(blank=True, null=True, max_length=120)
liked = models.ManyToManyField(User, default=None, blank=True, related_name='liked')
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:post-detail', kwargs={'slug': self.slug})
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
class Meta:
verbose_name_plural = 'Blog Posts'
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
content = models.TextField(max_length=300)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.post}-{self.user}-Comment No.{self.pk}"
LIKE_CHOICES = (
('Like', 'Like'),
('Unlike', 'Unlike')
)
class Like(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
value = models.CharField(choices=LIKE_CHOICES, max_length=8)
created = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.post}-{self.user}-{self.value}"
Here is the models.py in Users App
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
This is what I have tried in the models.py in Users app but didn't work
# Get the no. of posts
def get_posts_no(self):
return self.author.all().count()
# Get the no. of likes
def get_likes_given(self):
likes= set.like_set.all()
total_liked=0
for item in likes:
if item.value=='Like':
total_liked+=1
return total_liked
"... to add this information in the admin.py" → do you mean to say that you would like to show an additional column in the Django admin site with said information? If so, then the answer is:
In your admin.py, extend the PostAdmin class with a custom queryset and add comments_count and likes_count to list_display = (...):
from django.contrib import admin
from django.db.models import Count
from .models import Post
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'date_posted', 'comments_count', 'likes_count')
def get_queryset(self, request):
queryset = super().get_queryset(request)
queryset = queryset.annotate(
_comments_count=Count('comment_set', distinct=True),
_likes_count=Count('like_set', distinct=True)
)
return queryset
def comments_count(self, obj):
if hasattr(obj, '_comments_count'):
return obj._comments_count
return None
def likes_count(self, obj):
if hasattr(obj, '_likes_count'):
return obj._likes_count
return None
comments_count.admin_order_field = '_comments_count'
likes_count.admin_order_field = '_likes_count'
admin.site.register(Post, PostAdmin)
When a user creates a post using the CreateView I want it so when a user submits the Post, they then see the post they just made. But for some reason my get_absolute_url() is not working.
Prior to this I started to work on slugifying the Post and Category models and haven't been able to see if they work due to the fact get_absolute_url won't work.
Models:
class Category(models.Model):
title = models.CharField(max_length=200)
colorcode = models.CharField(max_length=20, blank=True, null=True)
description = models.TextField()
image = models.ImageField(blank=True, null=True)
slug = models.SlugField(unique=True)
def __str__(self):
return self.title
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, blank=True, null=True)
image = models.ImageField(blank=True, null=True)
live = models.BooleanField(default=False)
slug = models.SlugField(unique=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'slug': self.slug})
def create_slug(instance, new_slug=None):
slug = slugify(instance.title)
if new_slug is not None:
slug = new_slug
qs = Post.objects.filter(slug=slug).order_by("-pk")
exists = qs.exists()
if exists:
new_slug = "%s-%s" %(slug, qs.first().pk)
return create_slug(instance, new_slug=new_slug)
return slug
def pre_save_post_reciever(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = create_slug(instance)
pre_save.connect(pre_save_post_reciever, sender=Post)
Views:
class IndexView(ListView):
model = Post
queryset = Post.objects.filter(live=True)
template_name = "public/index.html"
class PostEdit(object):
model = Post
fields = '__all__'
success_url = '/'
class PostCreateView(LoginRequiredMixin, PostEdit, CreateView):
fields = ['title', 'text', 'category', 'image']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin, PostEdit, UpdateView):
fields = ['title', 'text', 'category', 'image']
Anyone has any idea on how to fix this?
If behaves according to this definition:
class PostEdit(object):
model = Post
fields = '__all__'
success_url = '/' # <<<---
In your case success_url is not static and cannot be defined as attribute. You have to override get_success_url instead like that:
def get_success_url(self):
return self.get_object().get_absolute_url()
also have a look at this answer.
upd
If the redirect is that simple then, as Daniel Roseman mentioned, you don't need to specify success_url at all - what you want is the default behavior.
You don’t even need to provide a success_url for CreateView or UpdateView - they will use get_absolute_url() on the model object if available.
I'm new Django and have been working on project. When I click on my techposts. it says No Techpost matches the given query. It was working fine and I didn't change anything. Not sure what could have happened. It has happened 2-3 times. Database flush used to fix this issue. But now nothing is working. Please suggest how do I fix this and the possible reason behind this?
PS: Found similar questions but their answers didnt help.
CODE:
models.py
class PublishedManager(models.Manager):
def get_queryset(self):
return super(PublishedManager, self).get_queryset().filter(status='published')
class Techpost(models.Model):
STATUS_CHOICS=(
('draft', 'Draft'),
('published', 'Published')
)
title = models.CharField(max_length=255, blank=False, null=False)
slug = models.SlugField(max_length=255, unique_for_date='publish')
author = models.ForeignKey(User, on_delete=models.CASCADE,
related_name='tech_posts')
body = models.TextField()
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10, choices = STATUS_CHOICS, default='draft')
tags = TaggableManager()
class Meta:
ordering = ('-publish',)
def __str__(self):
return self.title
objects = models.Manager() # default manager
published = PublishedManager() # Custom Manager.
def get_absolute_url(self):
return reverse('rincon:techpost_details', args=[
self.publish.year,
self.publish.strftime('%m'),
self.publish.strftime('%d'),
self.slug
])
class Comment(models.Model):
techpost = models.ForeignKey(Techpost, on_delete=models.CASCADE,
related_name='comments')
comment = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now =True)
active = models.BooleanField(default=True)
class Meta:
ordering = ('created',)
def __str__(self):
return "Comment on {}".format(self.techpost)
views.py
#login_required
def techpost_details(request, year, month, day, techpost):
techpost = get_object_or_404(Techpost, slug=techpost, status='published',
publish__year=year, publish__month=month, publish__day=day)
comments = techpost.comments.filter(active=True)
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.techpost = techpost
new_comment.save()
comment_form = CommentForm()
else:
comment_form = CommentForm()
techpost_tags_ids = techpost.tags.values_list('id', flat=True)
similar_techposts = Techpost.published.filter(tags__in=techpost_tags_ids)\
.exclude(id=techpost.id)
similar_techposts = similar_techposts.annotate(same_tags=Count('tags'))\
.order_by('-same_tags', '-publish')[:4]
return render(request, 'rincon/details.html', {'techpost': techpost, 'comments': comments, 'comment_form': comment_form, 'similar_techposts': similar_techposts})
urls.py
app_name = 'rincon'
urlpatterns =[
path('', views.home, name='home'),
path('submit/', views.request_submit, name='request_submit'),
path('techposts/', views.techpost_list, name='techpost_list'),
path('tag/<slug:tag_slug>/', views.techpost_list, name='techpost_list_by_tag'),
path('<slug:techpost_id>/share/', views.techpost_share, name='techpost_share'),
path('search/', views.techpost_search, name='techpost_search'),
path('hotfixes/', views.hotfixes, name='hotfixes'),
path('autocomplete/', views.autocomplete, name='autocomplete'),
path('<int:year>/<int:month>/<int:day>/<slug:techpost>/', views.techpost_details, name='techpost_details'),
I use to django for my web site. But ı have a question about blog/urls.py(my app name is blog )
I use to with one to many releationship in blog/models.py.
Category (1 => *) Subject (1 => *) Article.
class Category(models.Model):
name = models.CharField(max_length=200)
statement = models.TextField()
slug=models.SlugField()
page_name = models.ForeignKey('Page', on_delete=models.CASCADE)
def __str__(self):
return self.name
class Subject(models.Model):
name = models.CharField(max_length=200)
statement = models.TextField()
slug=models.SlugField()
category_name = models.ForeignKey('Category', on_delete=models.CASCADE)
def __str__(self):
return self.name
class Article(models.Model):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
title = models.CharField(max_length=200)
slug=models.SlugField()
text = models.TextField()
created_date = models.DateTimeField(
default=timezone.now)
published_date = models.DateTimeField(
blank=True, null=True)
subject_name = models.ForeignKey('Subject', on_delete=models.CASCADE)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:detail', kwargs={'id' : self.id})
blog/views.py
def detail(request,article_slug):
article = get_object_or_404(Article, slug=article_slug)
article_list=Article.objects .all()
subject_list = Subject.objects.all()
context={
'article': article,
'article_list':article_list,
'subject_list': subject_list
}
return render(request, 'blog/detail.html', context)
blog/urls.py
url(r'^(?P<category_slug>[\w-]+)/(?P<subject_slug>[\w-]+)/(?P<article_slug>[\w-]+)/$', views.detail, name='detail'),
I want to see the url when I click on the links of my article
http://127.0.0.1:8000/myworkandresearch/category_slug/subject_slug/article_slug
blog / urls.py 'How can I edit?
edit my blog/models.py
class Article(models.Model):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
title = models.CharField(max_length=200)
slug=models.SlugField()
text = models.TextField()
created_date = models.DateTimeField(
default=timezone.now)
published_date = models.DateTimeField(
blank=True, null=True)
subject_name = models.ForeignKey('Subject', on_delete=models.CASCADE)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:detail', args=[self.slug, self.subject.slug, self.subject.category.slug])
blog/views.py
def detail(request, category_slug, subject_slug, article_slug):
article = Article.objects.filter(
slug = article_slug,
subject__slug = subject_slug,
subject__category__slug = category_slug
)
subject_list = Subject.objects.all()
category_list = Category.objects.all()
context = {
'category_list': category_list,
'subject_list': subject_list,
'article': article
}
return render(request, 'blog/detail.html', context)
blog/urls.py
from django.conf.urls import include, url
from . import views
app_name = 'blog'
urlpatterns = [
url(r'^$', views.myworkandresearch, name='myworkandresearch'),
url(r'(?P<category_slug>[\w-]+)/$', views.subjects, name='subjects'),
url(r'^(?P<category_slug>[\w-]+)/(?P<subject_slug>[\w-]+)/(?P<article_slug>[\w-]+)/$', views.detail, name='detail'),
]
subjects.html
{% for article in subject.article_set.all %}}
<ul class="sidebar-items">
<li>{{article.title}}</li>
</ul>
{% endfor %}
[error continues.][2]
Page not found (404) Request Method: GET Request
URL: http://127.0.0.1:8000/myworkandresearch/machine_learning/python_dictionary/numpy/ Raised by: blog.views.subjects No Category matches the given query.
You're seeing this error because you have DEBUG = True in your Django
settings file. Change that to False, and Django will display a
standard 404 page.
I think you should have a detail view as
def detail(request, category_slug, subject_slug, article_slug):
article = Article.objects.filter(
slug = article_slug,
subject__slug = subject_slug,
subject__category__slug = category_slug
)
return render(request, 'blog/detail.html', {'article': article})
You need the following method on your article model
def get_absolute_url(self):
return reverse('detail', args=[self.slug, self.subject.slug, self.subject.category.slug])
When displaying your articles
{{article.title}}
Your url might look like this
url(r'^myworkandreseach/(?P<category_slug>[\w-]+)/(?P<subject_slug>[\w-]+)/(?P<article_slug>[\w-]+)/$', views.detail, name='detail'),
Be sure to import views
Im trying to filter posts belonging to a certain theme. I have a many-to-many relationship as you can see in my models. The problem is that I don't know how to filter. Normally I would do that by ID, but that didnt work.
Models:
class Theme(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(_('slug'), max_length=255, null=True, blank=True)
text = models.TextField()
created_date = models.DateTimeField(
default=timezone.now)
image = FilerImageField()
def publish(self):
self.save()
def __unicode__(self):
return self.title
class Post(models.Model):
writer = models.ForeignKey(Author, blank=True, null=True)
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(
default=timezone.now)
published_date = models.DateTimeField(
blank=True, null=True)
themes = models.ManyToManyField(Theme)
def publish(self):
self.published_date = timezone.now()
self.save()
def __unicode__(self):
return self.title
Views:
from .models import Theme, Post
from django.views.generic import ListView, DetailView
class ThemesOverview(ListView):
"""
Overview of all themes
"""
model = Theme
template_name = 'content/theme_list.html'
def get_queryset(self):
queryset = Theme.objects.all()
return queryset
class ThemePostsOverview(ListView):
"""
Overview of all posts within a theme
"""
model = Post
template_name = 'content/theme_posts_list.html'
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(ThemePostsOverview, self).get_context_data(**kwargs)
slug = self.kwargs['theme']
theme = Theme.objects.get(title=slug)
context['theme'] = theme
return context
def get_queryset(self):
queryset = Post.objects.all()
return queryset
As you can see I'm currently showing all posts instead of only the posts that belong to the theme
As we said in the comments, you need to filter the queryset by using the reverse relation on the theme. Here's one way of doing that:
class ThemePostsOverview(ListView):
model = Post
template_name = 'content/theme_posts_list.html'
def get_context_data(self, **kwargs):
context = super(ThemePostsOverview, self).get_context_data(**kwargs)
context['theme'] = self.theme
return context
def get_queryset(self):
slug = self.kwargs['theme']
self.theme = Theme.objects.get(title=slug)
return self.theme.post_set.all()