I'm trying to make a form where user can submit a post/article.
I have this model:
class Post(models.Model):
title = models.CharField(max_length=150)
content = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
This form:
class PostForm(forms.ModelForm):
title = forms.CharField(required=True)
content = forms.CharField(widget=forms.Textarea)
class Meta:
model = Post
fields = ['title', 'content']
And this view:
class PostCreateView(FormView):
form_class = PostForm
template_name = 'template/create_post.html'
success_url = reverse_lazy('index')
def form_valid(self, form):
response = super(PostCreateView, self).form_valid(form)
form.user = self.request.user
form.save()
return response
I've set the user, but I'm still getting
IntegrityError at /new-post/
NOT NULL constraint failed: cyth_post.user_id
Here's the full traceback call: http://dpaste.com/0PJSTY2
What am I doing wrong here?
Thanks in advance for answers!
You need set form.instance user and then run super
def form_valid(self, form):
form.instance.user = self.request.user
return super(PostCreateView, self).form_valid(form)
Related
I have a function in my Post model, a get_absolute_url which is a reverse for 'post-detail', it has kwargs for primary key , how do i add another for slug; which is in my Profile model.
At the moment I'm getting error :
NoReverseMatch at /post/26/tested123/update/
Reverse for 'post-detail' with keyword arguments '{'pk': 26}' not found. 1 pattern(s) tried: ['post/(?P[0-9]+)/(?P[-a-zA-Z0-9_]+)/$']
This error occurs when I click the update button on my update post page
feed model
feed/models.py
class Post(models.Model):
description = models.TextField(max_length=255)
pic = models.ImageField(upload_to='path/to/img', blank=True)
date_posted = models.DateTimeField(default=timezone.now)
user_name = models.ForeignKey(User, on_delete=models.CASCADE)
tags = models.CharField(max_length=100, blank=True)
def __str__(self):
return self.description
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
profile model
users/models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.png', upload_to='profile_pics')
slug = AutoSlugField(populate_from='user')
bio = models.CharField(max_length=255, blank=True)
friends = models.ManyToManyField('Profile', blank=True)
def __str__(self):
return str(self.user.username)
def get_absolute_url(self):
return "/users/{}".format(self.slug)
views.py
#login_required
def post_detail(request, pk, slug):
post = get_object_or_404(Post, pk=pk)
user = request.user
is_liked = Like.objects.filter(user=user, post=post)
if request.method == 'POST':
form = NewCommentForm(request.POST)
if form.is_valid():
data = form.save(commit=False)
data.post = post
data.username = user
data.save()
return redirect('post-detail', pk=pk, slug=slug)
else:
form = NewCommentForm()
return render(request, 'feed/post_detail.html', {'post':post, 'is_liked':is_liked, 'form':form})
views.py
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
fields = ['description', 'pic', 'tags']
template_name = 'feed/create_post.html'
def form_valid(self, form):
form.instance.user_name = self.request.user
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user == post.user_name:
return True
return False
Your get_absolute_url in Post doesn't have slug in kwargs or I'm missing something? 'slug': self.user_name.profile.slug
I am trying to let anyone post a comment on a blog but there are other fields in my model that need to get information that is not required in the form itself. I've tried adding it in the form_valid method but that method is not being called when the form is submitted. Any help is appreciated
views.py
class CommentView(CreateView):
template_name = "core/index.html"
model = Comment
form_class = CommentForm
def get_success_url(self):
return reverse_lazy("core:home")
def form_valid(self, form):
print("form is valid")
form.save()
return super().form_valid(form)
forms.py
class CommentForm(ModelForm):
class Meta:
model = Comment
fields = [
"text",
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["text"].widget.attrs.update(
{"class": "form-control", "placeholder": "Enter Your Comment Here",}
)
models.py
class Comment(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(
BlogPost, on_delete=models.CASCADE, related_name="comments"
)
text = models.TextField(max_length=255, default="")
date_posted = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"({self.author})'s comment on ({self.post.title})"
urls.py
app_name = "blog"
urlpatterns = [
path("post/<int:pk>/", BlogPostDetailView.as_view(), name="detail"),
path("post/category/<int:pk>/", CategoryListView.as_view(), name="category"),
path("post/comment/<int:pk>/", CommentView.as_view(), name="comment"),
]
You can try like this:
class CommentView(CreateView):
template_name = "core/index.html"
model = Comment
form_class = CommentForm
def get_success_url(self):
return reverse_lazy("core:home")
def form_valid(self, form):
form.instance.post_id = self.kwargs['pk']
form.instance.author = self.request.user
return super().form_valid(form)
Here I am assuming you are passing value of Post(primary key) via pk attribute of url andform.instance has the new(not yet saved to DB) Comment object. Then I am attaching the value of request.user(current user) and post id with that instance.
So i have this post module:
class Post(models.Model):
title = models.CharField(max_length=50)
content = models.TextField(max_length=255)
author = models.ForeignKey(User, on_delete=models.CASCADE)
date_pub = models.DateTimeField(timezone.now)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog-home')
and this comment module:
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
comment_author = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField(max_length=255)
def get_absolute_url(self):
return reverse('blog-home')
and this is my view for the comment:
class CreateComment(LoginRequiredMixin, CreateView):
model = Comment
template_name = 'my_blog/create_post.html'
fields = ['content']
def form_valid(self, form):
form.instance.comment_author = self.request.user
# form.instance.post_id =
return super().form_valid(form)
as for the urls:
/post/1 ===> will be post number 1
/post/1/comment ===> is the form the to post a new comment
I want the form.instance.post_id to be the post id that the comment belongs to.
How do I do that ?
I'm guessing you have a url pattern similar to this:
path('post/<int:pk>/comment', CreateComment.as_view(), name='create_comment')
Here's what I would do (assuming SingleObjectMixin works in combination with CreateView):
from django.views.generic.detail import SingleObjectMixin
class CreateComment(LoginRequiredMixin, CreateView, SingleObjectMixin):
model = Comment
template_name = 'my_blog/create_post.html'
fields = ['content']
def form_valid(self, form):
form.instance.comment_author = self.request.user
form.instance.post = self.get_object()
return super().form_valid(form)
form.instance.post_id = self.kwargs['pk']
was the thing i was looking for
I have a problem with the form in django. It uses the CheckboxSelectMultiple widget, but I need to set a list of possible choices only for posts created by the currently logged in user.
Any idea?
form:
class CycleForm(BSModalForm):
class Meta:
model = Cycle
fields = ['title', 'description', 'posts']
widgets = {
'posts': forms.CheckboxSelectMultiple(),
}
models
class Cycle(models.Model):
title = models.CharField(max_length=200, unique=True)
description = models.TextField(max_length=500, default="Brak opisu")
date_created = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
posts = models.ManyToManyField(Post)
def __str__(self):
return self.title
class Post(models.Model):
title = models.CharField(max_length=50, unique=True)
content = MDTextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
numbers_of_entries = models.IntegerField(default=0)
views
class CycleCreateView(BSModalCreateView, LoginRequiredMixin):
template_name = 'blog/cycle_form.html'
form_class = CycleForm
def form_valid(self, form, **kwargs):
form.instance.author = self.request.user
return super().form_valid(form)
def get_success_url(self):
reverse_user = self.request.user
return reverse('profile', kwargs={'username': reverse_user})
class CycleUpdateView(BSModalUpdateView):
model = Cycle
template_name = 'blog/cycle_update.html'
form_class = CycleForm
success_message = 'Success: Cycle was updated.'
def get_success_url(self):
reverse_user = self.request.user
return reverse('profile', kwargs={'username': reverse_user})
We can slightly alter the CycleForm form and add a user parameter. If that parameter is set, we filter the queryset of the posts field by only using Posts
class CycleForm(BSModalForm):
def __init__(self, *args, user=None, **kwargs):
super().__init__(*args, **kwargs)
if user is not None:
self.fields['posts'].queryset = Post.objects.filter(author=user)
class Meta:
model = Cycle
fields = ['title', 'description', 'posts']
widgets = {
'posts': forms.CheckboxSelectMultiple(),
}
Now we only need to pass the user to the form, we can override the get_form_kwargs method [Django-doc]:
class CycleCreateView(LoginRequiredMixin, BSModalCreateView):
template_name = 'blog/cycle_form.html'
form_class = CycleForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update(user=self.request.user)
return kwargs
def form_valid(self, form, **kwargs):
form.instance.author = self.request.user
return super().form_valid(form)
def get_success_url(self):
reverse_user = self.request.user
return reverse('profile', kwargs={'username': reverse_user})
You should do the same with the UpdateView. Note that your LoginRequiredMixin likely should be put first in the base classes.
I have DetaiView for my post and I want to use a form in this view so I decided to use DetailView with FormMixin. I need to set some initial to this form and I don't know how to do it. Here is my code:
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, related_name="comments", on_delete=models.CASCADE)
name = models.CharField("Nick", max_length=80)
email = models.EmailField()
body = models.TextField("Body")
created = models.DateTimeField("created", auto_now_add=True)
updated = models.DateTimeField("Updated", auto_now=True)
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = (
"name",
"email",
"body"
)
views.py
class PostDetailView(FormMixin, DetailView):
model = Post
form_class = CommentForm
template_name = "newspaper/post-detail.html"
def get_success_url(self):
return reverse("post-detail", kwargs={"slug": self.object.slug})
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context["form"] = self.get_form()
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
return super().form_valid(form)
So I want to post in CommentForm to post of this DetailView. I hope you understand :D.
Thanks in advance for the help!
With FormMixin you can specify form's initial using initial attribute:
class PostDetailView(FormMixin, DetailView):
model = Post
form_class = CommentForm
template_name = "newspaper/post-detail.html"
initial={'name': 'John'}
Or get_initial method:
def get_initial(self):
return {"post": self.get_object() }