Django's CreateView is not saving an object - python

I'm practicing Django's generic views, particularly ModelForms
These are my views and models
models.py
class Post(models.Model):
user = models.ForeignKey(User)
post_title = models.CharField(max_length=200)
post_content = models.CharField(max_length=500)
post_date = models.DateTimeField('date posted')
def get_absolute_url(self):
return reverse('user-detail', kwargs={'pk': self.pk})
def __unicode__(self):
return self.post_title
forms.py
class PostForm(forms.ModelForm):
post_title = forms.CharField(
label=u'Title',
widget=forms.TextInput(attrs={'size':64})
)
post_content = forms.CharField(
label=u'Content',
widget=forms.TextInput(attrs={'size':128})
)
class Meta:
model = Post
views.py
class PostCreate(CreateView):
fields = ['post_title', 'post_content']
template_name = 'app_blog/post_save_form.html'
model = Post
form_class = PostForm
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(PostCreate, self).dispatch(*args, **kwargs)
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.post_date = datetime.now()
return super(PostCreate, self).form_valid(form)
The view seems to work fine, not displaying any errors. However, when I check the Django admin page after submitting a form and saving a post, the post is not saved into the database for some reason.
Any idea why this is happening?

As I can see from your code you use
self.object = form.save(commit=False)
which mean that object will not be saved to database, but you can use it for futher processing. So you should use something like this:
self.object = form.save(commit=False) # Not hit database
self.object.user = self.request.user # Update user
self.object.post_date = datetime.now() # Update post_date
self.object.save() # And finally save your object to database.

Try this.
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_date = datetime.now()
return super(PostCreate, self).form_valid(form)

Related

What is causing this error in Django: NOT NULL constraint failed: mainapp_comment.post_id

I am in the middle of making a social network web application in Django. I have nailed most of the features needed, but I am stuck on making the commenting system work. I originally had it so users could make their own name for comments, but decided against that.
The posting system has it, so the user who is logged in and makes the post, their name will be displayed for the author of the post. That's why some of the code is copy-pasted between the 2.
I have tried all kinds of things, such as making the view a class instead of a function. It didn't work, and I am back to square one. Right now the user can make a post no problem, however the user's name isn't recorded and I have to set the author manually in the admin interface.
The relevant code in views.py
class CreatePostView(LoginRequiredMixin,CreateView):
login_url = '/login/'
redirect_field_name = 'mainapp/post_details.html'
form_class = PostForm
model = Post
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
#login_required
def add_comment_to_post(request,pk):
post = get_object_or_404(Post,pk=pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('mainapp:post_detail',pk=post.pk)
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
else:
form = CommentForm()
return render(request,'mainapp/comment_form.html',{'form':form})
Here is the relevant code from models.py
class UserProfileInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.CharField(max_length=150)
profile_pic = models.ImageField(upload_to='profile_pics',blank=True)
verified = models.BooleanField(default=False)
def __str__(self):
return self.user.username
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=75)
text = models.TextField(max_length=250)
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True,null=True,auto_now_add=True)
tags = TaggableManager()
def publish(self):
self.published_date = timezone.now()
self.save()
def approve_comments(self):
return self.comments.filter(approved_comment=True)
def get_absolute_url(self):
return reverse('mainapp:post_detail',kwargs={'pk':self.pk})
def __str__(self):
return self.title
class Comment(models.Model):
post = models.ForeignKey('mainapp.Post',related_name='comments',on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=True)
def approve(self):
self.approved_comment = True
self.save()
def get_absolute_url(self):
return reverse('post_list')
def __str__(self):
return self.text
Here is the forms.py file, with the CommentForm class
class CommentForm(forms.ModelForm):
class Meta():
model = Comment
fields = ('text',)
widgets = {
'text':forms.Textarea(attrs={'class':'textareaclass'}),
}
Here is the bit in views.py that makes the Post system thing I was talking about work
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
I am so sorry if this question if long, but thank you in advance :)
At the moment, I am just confused at why this doesn't work. It works for the post system, but I can't really figure out this comment system.
Thanks :)
I'm a little confused by the title of your post - I'm not sure why you would be getting a not null constraint failed on post_id when you're setting comment.post - but the following should work to attach the current user as the author of the comment:
#login_required
def add_comment_to_post(request,pk):
post = get_object_or_404(Post,pk=pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.author = request.user # add this line
comment.save()
return redirect('mainapp:post_detail',pk=post.pk)
# remove `def form_valid`
else:
form = CommentForm()
return render(request,'mainapp/comment_form.html',{'form':form})
Note the removal of the form_valid inline function definition which only makes sense as a method in the context of a class-based view.

Post comment inside Django DetailView

I need to add comments in article page. Post request receives data but not saves it.
In models.py:
class TemporaryComment(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='temporary_comment')
email = models.EmailField()
message = models.TextField(max_length=1500)
timestamp = models.DateTimeField(auto_now_add=True)
is_approved = models.BooleanField(default=True)
In forms.py:
class CommentModelForm(forms.ModelForm):
class Meta:
model = TemporaryComment
fields = [
'article',
'email',
'message',
]
in views.py:
class ArticleDetailView(FormMixin, DetailView):
model = Article
template_name = 'article.html'
form_class = CommentModelForm
def get_success_url(self):
return reverse_lazy('main:article', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['last_articles'] = Article.objects.filter(is_active=True).order_by('-timestamp')[:10]
context['comments'] = self.object.temporary_comment.filter(is_approved=True)
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):
form.save()
return super().form_valid(form)
If i comment out article field in forms.py, i got this error:
null value in column "article_id" violates not-null
constraint. DETAIL: Failing row
contains (18, username#email.com, check message, 2019-01-20 18:35:36.615955+00, t, null).
Help will be gladly accepted. Thanks for your time.
You should comment it out, and set it in form_valid.
def form_valid(self, form):
form.instance.article = self.object
form.save()
return super().form_valid(form)

How to tag a comment with the blog post in django?

i am new to django and am currently working on developing a blog.
I am trying to insert a comment form for my viewers using the CreateView template. Viewers will need to log in to type their comment. Their username will be tagged to the comment for the respective blog without them needing to indicate anything. There are no errors but data is not stored as expected. Comment is accepted but does not appear on the blog. Went to check on django administration where apparently only the author is stored but not the blog post that comment is on. Below is my code that is stored in the (views.py).
class CommentCreate(LoginRequiredMixin,CreateView):
model = Comment
fields = ['comment','blog']
template_name = 'catalog/blog_comment.html'
def get_context_data(self, **kwargs):
context = super(CommentCreate, self).get_context_data(**kwargs)
context['blog'] = get_object_or_404(Blog, pk = self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.author = self.request.user
return super(CommentCreate, self).form_valid(form)
def get_success_url(self):
return reverse('blog-detail', kwargs={'pk': self.kwargs['pk'],})
The models Blog and Comment is defined as below in a separate file (models.py)
class Blog(models.Model):
title = models.CharField(max_length=200)
blogger = models.ForeignKey('Blogger', on_delete=models.SET_NULL,
null=True)
content = models.TextField(max_length=1000, help_text='Write some stuffs
for your blog')
post_date = models.DateField(default=date.today)
class Meta:
ordering = ['post_date',"title","blogger"]
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog-detail', args=[str(self.id)])
class Comment(models.Model):
comment = models.TextField(max_length=1000, help_text='Comment on the
blog')
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
blog = models.ForeignKey("Blog", on_delete=models.SET_NULL, null=True)
post_date = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-post_date']
def __str__(self):
return self.comment
Any advice is appreciated. Thank you.
Problem is solved. Turns out i forgot to save the data in the form_valid.
Appended code is listed as below.
class CommentCreate(LoginRequiredMixin,CreateView):
model = Comment
fields = ['comment','blog']
template_name = 'catalog/blog_comment.html'
def get_context_data(self, **kwargs):
context = super(CommentCreate, self).get_context_data(**kwargs)
context['blog'] = get_object_or_404(Blog, pk = self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.author = self.request.user
form.instance.blog = get_object_or_404(Blog, pk = self.kwargs['pk'])
return super(CommentCreate, self).form_valid(form)

Django inline forms cannot query, must be model instance

I'm trying to make inline forms with class based views, i followed the instrunctions from here. The only change i've made was to give self.request.user instead of self.object to instance:
models.py
class Client(models.Model):
user = models.OneToOneField(CustomUser)
translate = models.BooleanField(default=False)
class ClientData(models.Model):
client = models.ForeignKey(Client)
language = models.ForeignKey(Language)
forms.py
class ClientForm(ModelForm):
class Meta:
model = Client
fields = '__all__'
exclude = ['user', ]
class ClientDataForm(ModelForm):
class Meta:
model = ClientData
fields = '__all__'
exclude = ['client', ]
ClientFormSet = inlineformset_factory(Client, ClientData, form=ClientDataForm, extra=1)
views.py
class ClientView(LoginRequiredMixin, UpdateView):
model = Client
fields = '__all__'
success_url = reverse_lazy('core:index')
class ClientDataView(LoginRequiredMixin, UpdateView):
template_name = 'core/client_data.html'
model = ClientData
form_class = ClientDataForm
success_url = reverse_lazy('core:index')
def get_object(self, queryset=None):
profile = get_object_or_404(ClientData, client__user=self.request.user)
return profile
def get_context_data(self, **kwargs):
context = super(ClientDataView, self).get_context_data(**kwargs)
if self.request.POST:
context['client_data'] = ClientFormSet(self.request.POST, instance=self.get_object())
else:
context['client_data'] = ClientFormSet(instance=self.get_object())
return context
def form_valid(self, form):
context = self.get_context_data()
client_data = context['client_data']
with transaction.atomic():
self.object = form.save()
if client_data.is_valid():
client_data.instance = self.object
return super(ClientDataView, self).form_valid(form)
Whenever i try to enter the page i get:
ValueError: Cannot query "asd#gmail.com": Must be "Client" instance.
[13/Dec/2017 15:48:36] "GET /client-data/ HTTP/1.1" 500 143759
for this line:
context['client_data'] = ClientFormSet(instance=self.get_object())
Your get_object is returning a ClientData instance.
def get_object(self, queryset=None):
profile = get_object_or_404(ClientData, client__user=self.request.user)
return profile
However, as the error suggests, the instance should be a Client instance, for example:
def get_object(self, queryset=None):
profile = get_object_or_404(Client, user=self.request.user)
return profile
Or you can simply follow the one-to-one relation backwards:
def get_object(self, queryset=None):
return self.request.user.client

formset validation in CBV

I have an Order form where I used formsets because I want the user to dynamically add more products and their quantity.
The user enters the name of the order, and it is validated not to be empty.
How do I validate the formset itself? to check that the product/quantity is not empty
views.py
class OrderCreateView(CreateView):
model = Order
template_name = "orderform.html"
fields = ['name', ]
def get_context_data(self, **kwargs):
data = super(OrderCreateView, self).get_context_data(**kwargs)
if self.request.POST:
data['productmetas'] = InlineOrderFormSet(self.request.POST)
return data
def form_valid(self, form):
context = self.get_context_data()
productmetas = context['productmetas']
self.object = form.save(commit=False)
self.object.save()
if productmetas.is_valid():
productmetas.instance = self.object
productmetas.save()
return super(OrderCreateView, self).form_valid(form)
models.py
class ProductMeta(models.Model):
order = models.ForeignKey(Order)
product = models.ForeignKey(Product)
quantity = models.FloatField()
forms.py
InlineOrderFormSet = inlineformset_factory(Order, ProductMeta,
form=OrderAutoCompleteForm,
extra=1)
For anyone interested in the working solution for the question above
adapted from the answer by #neverwalkaloner
def form_valid(self, form):
context = self.get_context_data()
productmetas = context['productmetas']
with transaction.atomic():
self.object = form.save()
if form.is_valid():
if productmetas.is_valid():
productmetas.instance = self.object
productmetas.save()
else:
return super(OrderCreateView, self).form_invalid(form)
return super(OrderCreateView, self).form_valid(form)

Categories