models.py
:
class BlogPost(models.Model):
title = models.CharField(max_length=200)
post_body = models.TextField()
user = models.ForeignKey(User)
pub_date = models.DateTimeField('published')
def get_absolute_url(self):
return u'/entries/%d' % self.id
forms.py
:
class NewPostForm(forms.ModelForm):
class Meta:
model = BlogPost
fields = ('title', 'post_body')
So, I was trying to make a view for users to post a new post into blog. BlogPost model has 4 fields - title
, post_body
, user
, and pub_date
. User should be able to fill the title
and post_body
, while the other two should be filled automatically. First thing I tried was this:
class NewPostView(generic.edit.CreateView):
model = BlogPost
form_class = NewPostForm
def get_initial(self):
return {'pub_date': timezone.now(),
'user': self.request.user}
But the only thing it does is providing the default values for html form. After additional research this is the code that ended up working:
class NewPostView(generic.edit.CreateView):
model = BlogPost
form_class = NewPostForm
def form_valid(self, form):
obj = form.save(commit=False)
obj.user = self.request.user
obj.pub_date = timezone.now()
obj.save()
return HttpResponseRedirect(obj.get_absolute_url())
So, while overriding form_valid method worked, I don't feel like it's how it should be done maybe? Is there a better place to put this actions into? What is the conventional way of doing this?
You can override form_valid
but then call out to super().form_valid(form)
in the return statement. You won't have to worry about saving the form, saving the object, or returning the HttpResponseRedirtect. All of that will be handled by the super. All you need to do is set the values of the form.instance
that you want to control behind the scenes.
views.py
class NewPostView(generic.edit.CreateView):
model = BlogPost
form_class = NewPostForm
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.pub_date = timezone.now()
return super().form_valid(form)
That would be a clean approach to adding the values behind the scenes that doesn't require you to do any more logic than adding the values. You can find a reference to that logic in the django documentation at:
https://docs.djangoproject.com/en/2.1/topics/class-based-views/generic-editing/#models-and-request-user
Ideally, for the pub_date
you would want to set the field to record the creation time automatically with auto_now_add
.
models.py
class BlogPost(models.Model):
title = models.CharField(max_length=200)
post_body = models.TextField()
user = models.ForeignKey(User)
pub_date = models.DateTimeField('published', auto_add_now=True)
def get_absolute_url(self):
return u'/entries/%d' % self.id
You can find the documentation on the DateTimeField and its reference to the auto_add_now
parameter at link that follows. It also has a auto_add
that will update every time the entry is modified, in case you wanted a last_edited_date
field.
https://docs.djangoproject.com/en/2.2/ref/models/fields/#django.db.models.DateTimeField
Also, there is a possibility that your user could access the form without being logged in. You would either need to handle the case for self.request.user
not being set, or you can user the LoginRequiredMixin
to only allow logged in users to access this form. I have had an easier time using the mixin.
The resulting view.py
with all the above mentioned changes would look like the following.
views.py
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView
from .forms import NewPostForm
from .models import BlogPost
class NewPostView(LoginRequiredMixin, CreateView):
model = BlogPost
form_class = NewPostForm
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With