Django Class Based View (FormView) - python

class ReviewView(FormView):
form_class = ReviewForm
template_name = "review/review.html"
success_url = "/thank-you"
def form_valid(self, form):
form.save()
return super().form_valid(form)
this is the error
how can I fix it
AttributeError at /
'ReviewForm' object has no attribute 'save'

Forms don't have a save() method.
You need to use a ModelForm as that will then have a model associated with it and will know what to save where https://docs.djangoproject.com/en/dev/topics/forms/modelforms/
If you don't need to save anything that is fine.

Related

Django form_valid method

In my form's view I wonder what if I don't add product_form.save method in the code below and what if I add that:
def form_valid(self, form):
product_form = form.save(commit=False)
product_form.user = self.request.user
product_form.save() # what if I delete this?
return super().form_valid(form)
In what view do you use this? If it is a CreateView [Django-doc] or UpdateView [Django-doc], it is fine. For a (simple) FormView [Django-doc], it is not since a FormView does not save the form.
You however do not need to save the form with commit=False to retrieve the instance. A simple one-liner to set the user is:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView
class MyCreateView(LoginRequiredMixin, CreateView):
# …
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
The name product_form in product_form = form.save(commit=False) is also somewhat misleading: the .save() method does not return a Form, but a Product object, hence it is a Product. But you do not need to save that product in the database, since a CreateView and UpdateView will call the .save() function of the form to save the model together with many-to-many relations that are specified in the form.

Django, How to get object instance(id) before creating it in form_valid function of generics view?

When i creating Post model object i need to get it ID instantly and create a second model User_permission , how in this case can i pass to post variable the ID data of newly created post
class CreatePostView(LoginRequiredMixin, CreateView):
model = Post
form_class = PostForm
def form_valid(self, form):
obj = User_permission.objects.create(post=post)
obj.save()
return super().form_valid(form)
Objects do not have ids until saved. You would need something like
def form_valid(self, form):
response = super().form_valid(form) # saves object
obj = User_permission.objects.create(post=self.object)
obj.save()
return response
This example in the documentation shows use of self.object

Django - CreateView passing the created objects id to another CreateView

I've been trying to pass the values of a newly created object to the next CreateView so that a new child model can be created.
This is what happens:
User creates the Parent model through a CreateView
If the form is valid the success_url redirects to the CreateView of another Child model. The child model in order to be created, needs the id of the Parent Model (ForeignKey relationship).
Once the Child Model is created redirect to a completed page.
Below I have an example of my code.
class AddParentModelView(LoginRequiredMixin, CreateView):
model = ParentModel
template_name = "dashboard/add_parent_model.html"
form_class = ParentModelForm
success_url = '/REDIRECT_TO_CHILD_MODEL/'
def form_valid(self, form):
form.instance.owner = self.request.user
# I Also tried sessions:
# self.request.session['parent_id'] = form.instance.id
# But they return None:
# print(self.request.session["venue_id"])
return super().form_valid(form)
class AddChildModelView(LoginRequiredMixin, CreateView):
model = ChildModel
template_name = "dashboard/add_child_model.html"
form_class = ChildModelForm
success_url = '/thanks/'
What's the proper way to approach this? If possible, please explain your solutions.
Thanks in advance!
Define get_success_url() on AddParentModelView to return the child URL including the ID of the parent.
For example, if AddChildModelView has a URL like:
path('/<int:parent_id>/add_child', AddChildModelView.as_view(), name='add-child')
then define the method like:
def get_success_url(self):
return reverse('add-child', kwargs={'parent_id': self.object.id})

Django CreateView is not saving object

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)

How to set ForeignKey in CreateView?

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)

Categories