I'm practicing django Class-Based-View with a basic blog application.
For some reason, however, the CreateView for my Post model is not saving the post inside the database.
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')
forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
exclude = ('user', 'post_date')
views.py
class PostCreate(CreateView):
template_name = 'app_blog/post_save_form.html'
model = Post
form_class = PostForm
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_date = datetime.now()
return super(PostCreate, self).form_valid(form)
It displays content without generating any error, but when I check the admin page,
the post created by the CreateView is not saved in the database..
Any idea..??
Thanks
One tip: don't use exclude when defining forms, use fields, is more secure and the recommended way to do it.
The redirect is defined by get_success_url method. If you have in your model the method get_absolute_url CreateView will redirect to that URL, otherwise you can always override get_success_url in your view.
Using get_absolute_url:
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')
#permalink
def get_absolute_url(self):
return ('myurlname', (), {'myparam': something_useful})
Using get_success_url:
class PostCreate(CreateView):
template_name = 'app_blog/post_save_form.html'
model = Post
form_class = PostForm
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_date = datetime.now()
form.save()
return super(PostCreate, self).form_valid(form)
def get_success_url(self):
return reverse('myurlname', args=(somethinguseful,))
I think you will find this page very useful when working with CBVs:
http://ccbv.co.uk/projects/Django/1.5/django.views.generic.edit/CreateView/
the problem is that you are excluding fields that are mandatory, so it won't pass through your form validation.
You should pass this fields hidden with some default value, let the use fill them, set them to null=True or populate them before you access form_valid
I came across this question today after many years but those answer seems not correctly.
The main issue here is the form.instance is None for CreateView. So my approach is below as suggestion form django docs:
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
instance.post_date = datetime.now()
instance.save()
return redirect(self.get_success_url())
I think this is a simple case of not calling form.save(). When the form is validated, all of the checks are done, but it doesn't actually save the object in the database. To do that, you explicitly need to tell it to, via the save() method.
So you want:
class PostCreate(CreateView):
template_name = 'app_blog/post_save_form.html'
model = Post
form_class = PostForm
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_date = datetime.now()
form.save()
return super(PostCreate, self).form_valid(form)
Related
I implemented this functionality with using FBV, but when I'm trying to use CBV, Objects were created with empty user field.
views.py
class BlockCreate(CreateView):
model = TrainingBlock
template_name = 'training_room/create_block.html'
form_class = BlockForm
success_url = reverse_lazy('gym')
def set_user(self, form):
form.instance.user = self.request.user
return super(BlockCreate, self).set_user(form)
models.py
class TrainingBlock(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
duration = models.IntegerField(default=10)
if_ended = models.BooleanField(default=False)
def __str__(self):
return self.name
forms.py
class BlockForm(forms.ModelForm):
class Meta:
model = TrainingBlock
fields = '__all__'
exclude = ['user']
There is no .set_user method in a CreateView, hence the logic will never get invoked. You use .form_valid(…) [Django-doc] instead:
from django.contrib.auth.mixins import LoginRequiredMixin
class BlockCreate(LoginRequiredMixin, CreateView):
model = TrainingBlock
template_name = 'training_room/create_block.html'
form_class = BlockForm
success_url = reverse_lazy('gym')
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
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: Since PEP-3135 [pep], you don't need to call super(…) with parameters if the first parameter is the class in which you define the method, and the second is the first parameter (usually self) of the function.
I am trying to create a comment system for the blog portion of my app with Django. I have attempted to mix my detail view with the form mixin and I'm struggling a bit. When the form is submitted, it doesn't save and no error is present. If any of you can help I would greatly appreciate it.
Here is my View
class DetailPostView(FormMixin, DetailView):
model = Post
template_name = "blog/post_detail.html"
context_object_name = "posts"
form_class = CommentForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["form"] = CommentForm
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 get_success_url(self):
return reverse("post-detail", kwargs={"pk": self.object.pk})
The model
class Comment(models.Model):
comment = models.ForeignKey(Post, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
content = models.TextField()
author = models.CharField(max_length=50)
created_on = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ["-created_on"]
def __str__(self):
return self.title
The reason that this happens is because you construct a new form that you pass to the context data, as a result, it will not render any errors, since you construct a form without validating the request data and render that form, you thus do not display the form that rejected the data in the first place.
But you do not need to do that. Django's FormMixin [Django-doc] already takes care of that. You thus should not override the .get_context_data(…) method [Django-doc].
Another problem is that you did not save your form, you can override a the form_valid method, or you can inherit from ModelFormMixin [Django-doc].
Finally you better first create the form, and then assign self.object, otherwise it will pass this as an instance to the form:
from django.views.generic.edit import ModelFormMixin
class DetailPostView(ModelFormMixin, DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'posts'
form_class = CommentForm
# no get_context_data override
def post(self, request, *args, **kwargs):
# first construct the form to avoid using it as instance
form = self.get_form()
self.object = self.get_object()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def get_success_url(self):
return reverse('post-detail', kwargs={'pk': self.object.pk})
The post rationale was to permit all clients on my site to have the option to make post and news which is a success however shockingly while making a post all the entire aunthenticated client/users in the data set shows up for the specific confirmed client to browse and this are data I don't need the client/users to see when making post and I have no arrangement to eliminate the users Foreignkey from the post Model since it encourages me actualizing other site basic capacities and the Author functions. since the users/client will fill in as creator/author
class Post(models.Model):
image = models.ImageField(upload_to="images/")
title = models.CharField(max_length=150)
summary = models.CharField(max_length=250)
category = models.ForeignKey(PostCategory, on_delete=models.CASCADE)
content = RichTextUploadingField()
date_posted = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User, on_delete=models.CASCADE)
slug = models.SlugField(max_length=250, unique=True, blank = True)
def onlyselfuser(self, *args, **kwargs):
self.user = self.request.user
super().onlyselfuser(*args, **kwargs)
#please take a look at my view.py files
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = __all__
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
i will appreciate if someone can show me a way out where the login user( user=request.user) will only appear excluding other users on moder and i tried a OneToOneField still and it didnt work. i Don't want to see all site user in user but specific useri
You can do something like setting the fields you want to show:
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['image', 'title', ....] # Don't add user fields name here
def form_valid(self, form):
form.instance.user = self.request.user # You wrote instance.author but your model had it as user
return super().form_valid(form)
But this is very tedious. A better way would be to use a ModelForm and add the user field to exclude. Plus this allows you much more customization!:
from django.forms import ModelForm
class PostForm(ModelForm):
class Meta:
model = Post
exclude = ['user']
class PostCreateView(LoginRequiredMixin, CreateView):
form_class = PostForm
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
So this is what I have:
#models.py
class Story(models.Model):
title = models.CharField(max_length=150)
story = models.TextField()
page = models.ForeignKey(Page, on_delete=models.CASCADE)
#views.py
class StoryUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Story
fields = ['title', 'content']
def form_valid(self, form):
story = self.get_object()
if self.request.user in User.objects.filter(groups__name=story.page.name):
return super().form_valid(form)
else:
pass
def test_func(self):
story = self.get_object()
if self.request.user in User.objects.filter(groups__name=story.page.name):
return True
return False
#urls.py
path('edit_story/<int:pk>', StoryUpdateView.as_view(), name='update-story')
here I want to give access to this update view to a group of users. So my query should be like this: if the current user is in User.objects.filter(groups__name=story.page.name) group, then he should have access to update a story.
Now I believe that my form_valid() and test_func() method is wrong. But I can't find a way to make it right. What should be the right logic for doing this?
Also, to get the story, what should I do? Do I do story = self.get_object() as done here which possibly is not working or do I need to use the method get_object_or_404() and how to do that?
Any help will be much appreciated
I have a model:
class Article(models.Model):
text = models.CharField()
author = models.ForeignKey(User)
How do I write class-based view that creates a new model instance and sets author foreign key to request.user?
Update:
Solution moved to separate answer below.
I solved this by overriding form_valid method. Here is verbose style to clarify things:
class CreateArticle(CreateView):
model = Article
def form_valid(self, form):
article = form.save(commit=False)
article.author = self.request.user
#article.save() # This is redundant, see comments.
return super(CreateArticle, self).form_valid(form)
Yet we can make it short (thanks dowjones123), this case is mentioned in docs.:
class CreateArticle(CreateView):
model = Article
def form_valid(self, form):
form.instance.author = self.request.user
return super(CreateArticle, self).form_valid(form)
I just stumbled into this problem and this thread led me in the right direction (thank you!). Based on this Django documentation page, we can avoid calling the form's save() method at all:
class CreateArticle(LoginRequiredMixin, CreateView):
model = Article
def form_valid(self, form):
form.instance.author = self.request.user
return super(CreateArticle, self).form_valid(form)
Berislav's code in views.py doesn't work for me. The form is rendered as expected, with the user value in a hidden input, but the form is not saved (I don't know why). I have tried a slightly different approach, that works for me:
views.py
from django.views.generic import *
from myapp.forms import ArticleForm
from myapp.models import Article
class NewArticleView(CreateView):
model = Article
form_class = ArticleForm
def get_initial(self):
return {
"user": self.request.user
}
You should set up a CreateView using a ModelForm for that model. In the form definition, you set the ForeignKey to have the HiddenInput widget, and then use the get_form method on the view to set the value of your user:
forms.py:
from django import forms
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
widgets = {"user": forms.HiddenInput()}
views.py:
from django.views.generic import *
from myapp.forms import ArticleForm
from myapp.models import Article
class NewArticleView(CreateView):
model = Article
form_class = ArticleForm
def get_form(self, form_class):
initials = {
"user": self.request.user
}
form = form_class(initial=initials)
return form
There are answers that are mainly related to the User model foreign key. However, let's suppose a simple scenario in which there is a model Comment containing a foreign key of the Article model, and you need to have a CreateView for Comment where each comment will have a foreign key of the Article model. In that case, the Article id would probably be in the URL, for example, /article/<article-id>/comment/create/. Here is how you can deal with such a scenario
class CommentCreateView(CreateView):
model = Comment
# template_name, etc
def dispatch(self, request, *args, **kwargs):
self.article = get_object_or_404(Article, pk=self.kwargs['article_id'])
return super(CommentCreateView, self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
form.instance.article= self.article # if the article is not a required field, otherwise you can use the commit=False way
return super(CommentCreateView, self).form_valid(form)