I imagine the solution to this can be found i similar topics, however, it seems a little bit different from problems I have seen.
The redirect, in add_post view, should send me to the Post class, where I have a get_absolute_url.
But it happens that I get the following error message: get_absolute_url() missing 1 required positional argument: 'self'
I literally copied the coded form the "Django Projects Cookbook" in case you wanna check it out and try the code yourself.
Thank you.
from django.contrib.auth.decorators import user_passes_test
from django.shortcuts import redirect, render_to_response, get_object_or_404, render
from .models import Post
from .forms import PostForm, CommentForm
#user_passes_test(lambda u: u.is_superuser)
def add_post(request):
form = PostForm(request.POST or None)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect(Post) # redirect to Post class
return render(request, 'blog/add_post.html',{ 'form': form })
def view_post(request, slug):
post = get_object_or_404(Post, slug=slug)
form = CommentForm(request.POST or None)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect(request.path)
return render(request, 'blog/blog_post.html',{'post': post,'form': form,})
from django.db import models
from django.template.defaultfilters import slugify
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
text = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __unicode__(self):
return self.title
#models.permalink
def get_absolute_url(self): # get_absolute_url
return ('blog_post_detail', (), { 'slug' :self.slug, })
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
class Comment(models.Model):
name = models.CharField(max_length=42)
email = models.EmailField(max_length=75)
website = models.URLField(max_length=200, null=True, blank=True)
text = models.TextField()
post = models.ForeignKey(Post, on_delete=models.CASCADE)
created_on = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.text
You use the Post class, not its object to redirect. You thus need to use post in:
#user_passes_test(lambda u: u.is_superuser)
def add_post(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
form.instance.author = request.user
post = form.save()
return redirect(post)
return render(request, 'blog/add_post.html',{ 'form': form })
The #permalink decorator was removed in Django-2.1, it is advised to use reverse [Django-doc] instead:
from django.urls import reverse
class Post(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
text = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __unicode__(self):
return self.title
def get_absolute_url(self):
return reverse('blog_post_detail', kwargs={'slug': self.slug})
Furthermore python-2.x is no longer supported since January 1, 2020; so you might want to update to django-3.x in a python-3.x environment.
Related
'ReverseManyToOneDescriptor' object has no attribute 'filter'
why?)
I'm trying to create a blog in django, got to the stage of adding comments and categories and got stuck
I get an error.
views.py
from django.shortcuts import render,
get_object_or_404
from .models import Post, Comment
from django.views import generic
from django.http import HttpResponse
from .forms import CommentForm
def blog_list(request):
post = Post.objects.all().order_by('-date')
return render(request,'blog/blog_list.html', {'posts':post})
def blog_detail(request, slug):
#return HttpResponse(slug)
detail_post = Post.objects.get(slug=slug)
comments = Post.comments.filter(active=True)
new_comment = None
# 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,'blog/blog_detail.html', {'detail_post':detail_post, 'comments':comments, 'new_comment': new_comment, 'comment_form': comment_form})
I hope someone helps, also with the problem of adding a category
14.comments = Post.comments.filter(active=True) …
▶ Local vars
here is my
models.py
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField()
body = models.TextField()
date = models.DateTimeField(auto_now_add=True)
first_src = models.CharField('первоисточник', blank=True, max_length=100)
author = models.ForeignKey(User, on_delete= models.CASCADE )
thumb = models.ImageField(default='default.png', blank=True)
# add AND GO TO MIGRATE AND MAKEMIGRATIONS !!!
class Meta:
ordering = ['-date']
def __str__(self):
return self.title
def snippet(self):
return self.body[:50]+'...'
"""def get_absolute_url(self):
from django.urls import reverse
return reverse("post_detail", kwargs={"slug": str(self.slug)})"""
class Comment(models.Model):
post = models.ForeignKey(Post,
on_delete=models.CASCADE,
related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField()
body = 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 by {} on {}'.format(self.name, self.post)
What's wrong?
I hope someone helps, also with the problem of adding a category
You're trying to filter on the class (Post), but want to filter on the specific model (detail_post):
comments = detail_post.comments.filter(active=True)
I am trying to allow user to post answer and comments to a particular bloq question
Q&A
Error displayed:
ValueError at /adding-url-patterns-for-views
Cannot assign "<SimpleLazyObject<django.contrib.auth.models.AnonymousUser object at 0x04A5B370>>": "PostAnswer.user" must be a "CustomUser" instance.
Here is my model
class PostQuestion(models.Model):
""" Model for posting questions """
title = models.CharField(max_length=100, unique=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE, null=True, blank=True)
slug = models.SlugField(unique=True, null=True, max_length=250)
created_on = models.DateTimeField('date published',
auto_now_add=True)
text_content = models.TextField()
tags = TaggableManager()
class Meta:
ordering = ['-created_on']
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('detail_view', kwargs={'slug': self.slug})
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
return super().save(*args, **kwargs)
class PostAnswer(models.Model):
""" Model for answering questions"""
question = models.ForeignKey(
PostQuestion,
on_delete=models.CASCADE,
related_name='comments',
)
text_content = models.TextField()
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
)
approved_comment = models.BooleanField(default=False)
created_on = models.DateTimeField('published', auto_now_add=True)
class Meta:
ordering = ['created_on']
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return 'comment by {} on {}'.format(self.user, self.question)
Here is my views.py on an app
class Index(ListView):
queryset = PostQuestion.objects.all()
template_name = 'index.html'
context_object_name = 'posts'
paginate_by = 10
def detail(request, slug):
post = get_object_or_404(PostQuestion, slug=slug)
comments = post.comments.all()
# comment posted
if request.method == 'POST':
form = CommentForm(data=request.POST)
if form.is_valid():
form.instance.question = post
form.instance.user = request.user
form.save()
return redirect('post_detail', slug=slug)
else:
form = CommentForm()
context = {'post': post,
'comments': comments,
'form': form}
return render(request, 'detail.html', context)
My App form:
class CommentForm(forms.ModelForm):
text_content = forms.CharField(widget=PagedownWidget())
class Meta:
model = PostAnswer
fields = ['text_content']
My urls.py view for the app
from .views import detail, ask, Index
from django.urls import path
urlpatterns = [
# path('<slug:slug>/comment', add_comment_to_post, name='add_comment_to_post'),
path('ask/', ask, name='ask'),
path('<slug:slug>', detail, name='detail_view'),
path('', Index.as_view(), name='index'),
]
Here is the html template for the comment
<!--------Display a form for users to add a new comment------->
<h3>Leave a comment</h3>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary btn-sm">Add Comment</button>
</form>
This is all of code that I think you need to know the problem. I don't know if the problem can be caused for database or why are not all instructions in the views.
If you can help me, thanks you in advance.
The name of the field is question, not post, so you should rewrite the logic to:
new_comment.question = post
This will however still raise an error, since the user field is not filled in either.
Using commit=False is however not a good idea, since then the form can no longer save many-to-many fields. As far as I can see, there are at the moment no many-to-many fields, but nevertheless, if you later add such fields, you have to rewrite the logic. You can improve the view to:
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
#login_required
def detail(request, slug):
post = get_object_or_404(PostQuestion, slug=slug)
comments = post.comments.all()
if request.method == 'POST':
form = CommentForm(data=request.POST)
if form.is_valid():
form.instance.question = post
form.instance.user = request.user
form.save()
return redirect('page:detail_view', slug=slug)
else:
form = CommentForm()
context = {
'post': post,
'comments': comments,
'form': form
}
return render(request, 'detail.html', context)
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
When I try to save a post, the post is saved, but current user is not registered and the post is duplicated with a blank entry and the current user is not stored.
For adding the post I use not the admin app but a personal template and form.
See the problem:
This is my view code:
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from .forms import NewAdminPostForm
from .models import Post, Category
# Create your views here.
def home(request):
posts = Post.objects.all()
categories = Category.objects.all()
posts_last = Post.objects.order_by('-created_at')[0:3]
return render(request, 'front/blog-list.html', {'posts': posts,
'categories': categories, 'posts_last': posts_last})
#login_required
def newadminpost(request):
if request.method == 'POST':
form = NewAdminPostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
Post.objects.create(
message=form.cleaned_data.get('message'),
category_id=post.category_id,
created_by=request.user
)
#post.save()
return redirect('listadminpost')
else:
form = NewAdminPostForm()
return render(request, 'back/new-post-blog.html', {'form': form})
#login_required
def listadminpost(request):
posts = Post.objects.all()
return render(request, 'back/list-post-blog.html', {'posts': posts})
Form of my Blog:
from django import forms
from .models import Post, Category
class NewAdminPostForm(forms.ModelForm):
title = forms.CharField(label="Titre de l'article", max_length=255,)
message = forms.CharField(widget=forms.Textarea(),
max_length=4000,
help_text="Contenu de l'article")
pre_message = forms.CharField(label="Message de prévisu",
widget=forms.Textarea(),
max_length=4000,
help_text="Contenu de l'article")
class Meta:
model = Post
fields = ['title','meta_desc','message','pre_message','category']
Model of my Blog:
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=255, unique=True)
description = models.TextField(max_length=1000)
def __str__(self):
return self.name
def get_categories_count(self):
return Category.objects.filter(post__category=self).count()
class Post(models.Model):
title = models.CharField(max_length=255)
meta_desc = models.TextField(max_length=320, null=True)
pre_message = models.TextField(max_length=4000, null=True)
message = models.TextField(max_length=4000)
category = models.ForeignKey(Category, on_delete='cascade')
created_at = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE,
blank=True, null=True)
def __str__(self):
return self.title
https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.create
your code:
post.save()...Post.objects.create(
from the link above:
A convenience method for creating an object and saving it all in one step. Thus:
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
and:
p = Person(first_name="Bruce", last_name="Springsteen")
p.save(force_insert=True)
are equivalent.
So what you do in your code:
you save post object created from form
you create and save another Post instance by calling create method
choose any of them, just one, and this will avoid duplicates.
Instead of doing this:
#login_required
def newadminpost(request):
if request.method == 'POST':
form = NewAdminPostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
Post.objects.create(
message=form.cleaned_data.get('message'),
category_id=post.category_id,
created_by=request.user
)
#post.save()
return redirect('listadminpost')
else:
form = NewAdminPostForm()
return render(request, 'back/new-post-blog.html', {'form': form})
You could do this: cleaner, easier to read, more up-to-date, and easier to maintain:
class AdminCreateView(LoginRequiredMixin, generic.CreateView):
model = Request
form_class = RequestForm
def form_valid(self, form):
result = super(AdminCreateView, self).form_valid(form)
title = form.cleaned_data.get('title')
meta_desc = form.cleaned_data.get('meta_desc')
message = form.cleaned_data.get('message')
# and so on. if there's something you refuse (ex title empty) do this:
if not title:
form.add_error('title', _("Precise the title"))
return self.form_invalid(form)
Post.objects.create(message=message,
meta_desc=meta_desc,
title=title,) # and so on
return result
I will be really grateful if anyone can help to resolve the issue below.
I have the following Django project coding. The problem is: when the browser was given "/posts/remove/<post_id>/" or "/posts/edit/(<post_id>/" as the url, it will allow the second user (not owner) to perform the remove and edit jobs, respectively.
How can I allow only the owner of a new post to edit or delete the post?
account.models.py:
from django.db import models
from django.conf import settings
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL)
def __str__(self):
return 'Profile for user {}'.format(self.user.username)
posts.models.py:
from django.db import models
from django.conf import settings
from django.utils import timezone
from django.utils.text import slugify
from django.core.urlresolvers import reverse
from taggit.managers import TaggableManager
class PublishedManager(models.Manager):
def get_queryset(self):
return super(PublishedManager, self).get_queryset().filter(status='published')
class Post(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
related_name='posts_created')
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique_for_date='created')
image = models.ImageField(upload_to='images/%Y/%m/%d', null=True, blank=True)
description = models.TextField(blank=True)
created = models.DateTimeField(default=timezone.now,
db_index=True)
updated = models.DateTimeField(auto_now=True)
users_like = models.ManyToManyField(settings.AUTH_USER_MODEL,
related_name='posts_voted',
blank=True)
status = models.CharField(max_length=10, default='published')
objects = models.Manager() # The default manager.
published = PublishedManager() # The Dahl-specific manager.
tags = TaggableManager()
class Meta:
ordering = ('-created',)
def __str__(self):
return self.title
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('posts:detail', args=[self.id, self.slug])
posts.view.py:
from django.views.decorators.http import require_POST
from django.shortcuts import render, redirect, get_object_or_404, render_to_response
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.conf import settings
from django.core.context_processors import csrf
from .forms import PostCreateForm, EmailPostForm, CommentForm, SearchForm
from .models import Post
from actions.utils import create_action
#login_required
def post_create(request):
"""
View for creating a new post.
"""
if request.method == 'POST':
# form is sent
form = PostCreateForm(data=request.POST, files=request.FILES)
if form.is_valid():
cd = form.cleaned_data
new_item = form.save(commit=False)
# assign current user to the item
new_item.user = request.user
tags = form.cleaned_data['tags']
new_item.save()
for tag in tags:
new_item.tags.add(tag)
new_item.save()
create_action(request.user, 'created a post:', new_item)
messages.success(request, 'Post added successfully')
form = PostCreateForm()
else:
messages.error(request, 'Error adding new post')
else:
# build form
form = PostCreateForm(data=request.GET)
return render(request, 'posts/post/create.html', {'section': 'posts',
'form': form})
#login_required
def post_remove(request, post_id):
Post.objects.filter(id=post_id).delete()
return redirect('posts:mypost')
#login_required
def post_edit(request, post_id):
item = Post.objects.get(pk=post_id)
if request.method == 'POST':
form = PostCreateForm(request.POST, instance=item)
if form.is_valid():
form.save()
return redirect('posts:mypost')
else:
form = PostCreateForm(instance=item)
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('posts/post/post_edit.html', args)
posts.urls.py
from django.conf.urls import url
from . import views
from .feeds import LatestPostsFeed
urlpatterns = [
url(r'^create/$', views.post_create, name='create'),
url(r'^remove/(?P<post_id>\d+)/$', views.post_remove, name='post_remove'),
url(r'^edit/(?P<post_id>\d+)/$', views.post_edit, name='post_edit'),
]
Add request.user == item.user check inside your method.
#login_required
def post_remove(request, post_id):
item = Post.objects.get(pk=post_id)
if request.user == item.user:
Post.objects.filter(id=post_id).delete()
return redirect('posts:mypost')
#login_required
def post_edit(request, post_id):
item = Post.objects.get(pk=post_id)
if request.user == item.user:
...
//write your code here
i create simple page where i can add article (title, text and category). When i do it on 'site administration everything is ok', but when i add it on page i can't choose category.
http://i.stack.imgur.com/4Nxzb.jpg
I choose category, for example like on this screen but my article do not have this category after i save it (article is uncategorized).
I created forms.py file and i done it like this:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'text', 'categories')
How must i change 'categories' here to make it usable?
models.py:
from django.db import models
from django.utils import timezone
class Category(models.Model):
name = models.CharField('Nazwa Kategorii', max_length=100)
slug = models.SlugField('Odnośnik', max_length=100)
icon = models.ImageField('Ikonka Kategorii', upload_to='icons',
blank=True)
class Meta:
verbose_name = "Kategoria"
verbose_name_plural = "Kategorie"
def __unicode__(self):
return self.name
class Post(models.Model):
author = models.CharField(max_length=25, blank=True, null=True)
title = models.CharField(max_length=200)
slug = models.SlugField('Odnośnik', max_length=255)
text = models.TextField()
published_date = models.DateTimeField(
default=timezone.now)
categories = models.ManyToManyField(Category, verbose_name='Kategorie')
def publish(self):
self.published_date = timezone.now()
self.save()
def __unicode__(self):
return self.title
And from views.py my view
def post_new(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect('blog.views.post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
When you save a form using commit=False you should call the save_m2m() method of the form:
post = form.save(commit=False)
post.author = request.user
post.save()
form.save_m2m()
Another, more elegant, solution is to pass the instance with preset field to the form's constructor. Is this case you don't need to use the commit=False argument:
form = PostForm(request.POST, instance=Post(author=request.user))
if form.is_valid():
post = form.save()
return redirect('blog.views.post_detail', pk=post.pk)