Inserting a 'hidden timestamp field' in a modelform? - python

I have a simple form that allows a user to create a post. The fields are post_title, post_content, post_date. The post_title and post_content will be provided by the user, however the post_date will be autogenerated by the system and will give the current timestamp. Due to this, I will only show both title and content in the form. When I try and submit a request however, this gives me an IntegrityError saying NOT NULL constraint failed: main_app_post.post_date. I am fairly new at Django so I really have no idea what to do. I want it that whenever the user sends a Post request (and if it passes the validations), it will generate the current timestamp to post_date before saving it to the database. Any advices in regards of this? Thanks a lot!
models.py:
class Post(models.Model):
post_title = models.CharField(max_length=100)
post_content = models.TextField(max_length=400)
post_date = models.DateTimeField()
def __str__(self):
return self.post_title
forms.py:
class PostForm(forms.ModelForm):
class Meta:
model = models.Post
fields = ('post_title', 'post_content')
views.py:
if request.method == 'POST':
form = forms.PostForm(request.POST)
if form.is_valid():
post_title = form.cleaned_data['post_title']
post_content = form.cleaned_data['post_content']
post_date = datetime.datetime.now().timestamp()
form.post_date = post_date
form.save(commit=True)

set auto_now_add=True
class Post(models.Model):
post_title = models.CharField(max_length=100)
post_content = models.TextField(max_length=400)
post_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.post_title
Refernce: Django auto_now and auto_now_add

You can specify a default and make it non-editable, but in fact Django already has a solution for this: you can specify auto_now_add=True [Django-doc]:
class Post(models.Model):
post_title = models.CharField(max_length=100)
post_content = models.TextField(max_length=400)
post_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.post_title
This will make the field non-editable, so it will not show up in a form by default, and furthermore it will use as default value the current timestamp. This thus means that the view and form no longer have to worry about this.

Related

Django: Why does my validation error not show? The title is a unique field that is checked under clean_title, but it just redirects to the profile?

So my form redirects to my profile page, and the form is saved if has a unique title, however this validation error is not raised on testing.
Essentially my slug field is a primary key of sorts, with it being generated from the title in views.py. I know its not the best solution to a primary key, but I thought an easy way to make it unique was to ensure the title was too on form submission.
I've tried the old generate a unique slug, but that dosent seem to work either.
Forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ["title", "content", "featured_image", "excerpt", "status"]
def clean_title(self):
title = self.cleaned_data['title']
if Post.objects.filter(title=title).exists():
raise forms.ValidationError("Title already exists")
return title
Views.py
class CreatePost(View):
def get(self, request):
return render(request, 'create_post.html', {
'post_form': PostForm,
})
def post(self, request):
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.slug = slugify(post.title)
post.save()
return HttpResponseRedirect(reverse('profile', args=[request.user]))
The Post Model from models.py
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"
)
featured_image = CloudinaryField('image', default='placeholder')
excerpt = models.TextField(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)
likes = models.ManyToManyField(
User, related_name='blogpost_like', blank=True)
class Meta:
ordering = ["-created_on"]
def __str__(self):
return self.title
def number_of_likes(self):
return self.likes.count()
please be nice im not a pro dev... yet :)
of course. You have always redirect:
def post(self, request):
...
if form.is_valid():
...
return HttpResponseRedirect(reverse('profile', args=[request.user]))
in your case it should be:
def post(self, request):
...
if form.is_valid():
...
return HttpResponseRedirect(reverse('profile', args=[request.user]))
return render(request, 'create_post.html', {'post_form': form})
At first, you have an error on get(). In context you send class PostFrom and not the instance of this class. Right now it works, the template create empty instance of class PostForm, but the logic is wrong.
My opinion, try to use GCBV in your case CreateView. It is easier and you can not do stupid errors: the CreateView do it for you. :)
Read more here:
https://docs.djangoproject.com/en/4.0/ref/class-based-views/generic-editing/#django.views.generic.edit.CreateView

How to use default field values for ForeignKey?

I'm just starting to learn Django and building a simple blog with it.
So i have two models Post and PostStatistics. When ever i add a new post, i want that PostStatistics contains all specified default values. How can i achieve this correctly?
models.py
class PostStatistics(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4)
post_views = models.IntegerField(default=0)
post_likes = models.IntegerField(default=0)
post_favorites = models.IntegerField(default=0)
class Post(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4)
user = models.ForeignKey(UserProfile, null=True, on_delete=models.CASCADE)
statistics = models.ForeignKey(PostStatistics, null=True, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
body = RichTextField(blank=True, null=True)
draft = models.BooleanField(default=False)
views.py
def add_post(request: HttpRequest):
form = PostForm(request.POST)
is_draft = True if form.data.get("draft") == "on" else False
post = Post(
title=form.data["title"],
body=form.data["post"],
user=request.user,
draft=is_draft,
statistics = PostStatistics() -> this is not correct
)
post.save()
return redirect("post")
At the moment i get FOREIGN KEY constraint failed.
You create a new one:
def add_post(request: HttpRequest):
form = PostForm(request.POST)
is_draft = form.data.get('draft') == 'on'
post_statistics = PostStatistics.objects.create()
Post.objects.create(
title=form.data['title'],
body=form.data['post'],
user=request.user,
draft=is_draft,
statistics = post_statistics
)
return redirect('post')
It however does not make much sense to store the statistics in a separate model, since there is a clear one-to-one relation, and thus the statistics can be stored in the Post model.
Furthermore you can use the form to validate the input and also create the object (or at least parts of it). A better modeling thus might be:
from django.conf import settings
class Post(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE, editable=False)
title = models.CharField(max_length=200)
body = RichTextField(blank=True, null=True)
draft = models.BooleanField(default=False)
post_views = models.IntegerField(default=0, editable=False)
post_likes = models.IntegerField(default=0, editable=False)
post_favorites = models.IntegerField(default=0, editable=False)
and then work with a ModelForm where you let the form do all the proper validation and cleaning:
from django.contrib.auth.decorators import login_required
#login_required
def add_post(request: HttpRequest):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
form.instance.user = request.user
form.save()
return redirect('post')
else:
form = PostForm()
return render(request, 'name-of-some-template.html', {'form': form})
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].

Cannot add "<Model: Model object (None)>": the value for field "field" is None

I am trying to save an object to my database while adding it to the Many to Many field of another object. I already tried many other solutions from here but nothing worked so far.
Model:
class SellerPost(models.Model):
post_uuid = models.UUIDField(default=uuid.uuid4, editable=False)
seller = models.ForeignKey("User", on_delete=models.CASCADE)
text_content = models.TextField()
comments = models.ManyToManyField("SellerPostComment", blank=True)
class SellerPostComment(models.Model):
comment_id = models.IntegerField(primary_key=True)
post = models.ForeignKey(SellerPost, on_delete=models.CASCADE)
addressed = models.ForeignKey("User", on_delete=models.CASCADE, null=False, related_name="seller_addressed_comment")
commenter = models.ForeignKey("User", on_delete=models.CASCADE, null=False)
content = models.TextField()
View (i cut everything but the essential part that has sth to do with the error):
post = request.POST["post"]
post_obj = SellerPost.objects.get(post_uuid=post)
comment = comment_form.save(commit=False)
comment.addressed = user
comment.commenter = request.user
comment.post = post_obj
comment.save()
post_obj.comments.add(comment)
return redirect(index)
class PostCommentForm(forms.ModelForm):
class Meta:
model = SellerPostComment
fields = ("content",)
def save(self, commit=True):
comment = super(PostCommentForm, self).save(commit=False)
if commit:
comment.save()
return comment
Error:
Cannot add "<SellerPostComment: SellerPostComment object (None)>": the value for field "sellerpostcomment" is None
The form is valid but it just won't save the comment to the M2M field of the post.
Thanks in advance!

How to get comment for the particular Blog Post in Django?

#Models.py
#BlogPost Model
class BlogPost(models.Model):
POST_CATEGORY = (
('Education','Education'),
('Tech','Tech'),
('Automobile','Automobile'),
('Other','Other')
)
title = models.CharField(max_length=150)
thumbnail = models.ImageField(upload_to='Blog Thumbnail')
category = models.CharField(max_length=100, choices = POST_CATEGORY )
content = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
slug = models.CharField(max_length=200, unique=True, null=True)
tags = models.CharField(max_length=150, null=True, blank=True)
writer = models.ForeignKey(User,on_delete=models.CASCADE,null=False)
def __str__(self):
return self.title
#BlogComment Model
class BlogComment(models.Model):
post = models.ForeignKey(BlogPost,on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
comment = models.TextField()
parent = models.ForeignKey('self',on_delete=models.CASCADE,null=True)
timestamp = models.DateTimeField(auto_now_add=True)
#Views Code
def blogPost(request, slug):
post = BlogPost.objects.filter(slug=slug)
'''How to get comment for particularly this post'''
comments = BlogComment.objects.filter(post=post) # It is giving a wrong answer
'''The error I am getting
ValueError: The QuerySet value for an exact lookup must be limited to one result using slicing.'''
print(comments)
context = {
'Post':post,
'Comments':comments
}
return render(request,'blogpost.html',context)
How to get the comment for the particulary for this blog post? The error I am getting -" ValueError: The QuerySet value for an exact lookup must be limited to one result using slicing."
objects.filter() returns a queryset.
The filter method is expecting an instance of BlogPost (to get the id) or a integer-id since objects.filter(post=pk)
Use objects.get(), that way you get a instance of BlogPost, not a queryset:
post = BlogPost.objects.get(slug=slug)
comments = BlogComment.objects.filter(post=post)
Additions:
You can also handle the exception that could happen if the post does not exist by different ways. One of them is returning Http404, and here is the easiest way to do that:
from django.shortcuts import get_object_or_404
post = BlogPost.objects.get_object_or_404(slug=slug)

keep getting integrityError when trying to implement django-multiupload

I keep getting the following integrityError when trying to implement the django-multiupload app:
null value in column "post_id" violates not-null constraint
DETAIL: Failing row contains (9, post_images/1899987_718836594813781_835045800_n.jpg, , null, null, null).
I am trying to allow my users to upload multiple pictures to a post. I think where I am going wrong is that it may be saving before being able to get the post id. In my views I have changed the form_valid which may also be effecting it.
Ideally I would like to pass the picture_author and project instances to each image as well, but to get started I have put them = null to figure out how to pass just the post instance at the moment.
Im new to coding & django, so any help in trying to solve it would be much appreciated!
models:
class ProjectPost(models.Model):
project = models.ForeignKey(UserProject)
title = models.CharField(max_length=100)
post_overview = models.CharField(max_length=1000)
date_created = models.DateTimeField(auto_now_add=True)
post_views = models.IntegerField(default=0)
post_likes = models.IntegerField(default=0)
post_author = models.ForeignKey(User, null=True)
class PostImages(models.Model):
post_picture = models.FileField(upload_to='post_images', blank=True)
post = models.ForeignKey(ProjectPost)
picture_description = models.CharField(max_length=100)
picture_author = models.ForeignKey(User, null=True)
project = models.ForeignKey(UserProject, null=True)
forms
class ProjectPostForm(forms.ModelForm):
class Meta:
model = ProjectPost
fields = ('title', 'post_overview')
files = MultiFileField(min_num=1, max_num=20, max_file_size=1024*1024*5)
def save(self, commit=True):
instance = super(ProjectPostForm, self).save(commit)
for each in self.cleaned_data['files']:
PostImages.objects.create(post_picture=each, post=instance)
return instance
views
class NewPost(CreateView):
model = ProjectPost
form_class = ProjectPostForm
template_name = 'howdidu/new_post.html'
def form_valid(self, form):
self.object = form.save(commit=False)
project = UserProject.objects.get(slug=self.kwargs["slug"])
self.object.project = project
form.instance.post_author = self.request.user
self.object.save()
return super(NewPost, self).form_valid(form)
def get_success_url(self):
project_username = self.request.user.username
project_slug = self.object.project.slug
return reverse('user_project', kwargs={'username':project_username, 'slug': project_slug})
You have to save the post before you can add images to it. You need to explicitly tell the get_or_create what the post_id is supposed to be before an image will be saved, and I dont think you're doing that in your code.

Categories