Once the user has made a valid post and pressed Post, I want them to be taken to the valid post. I ran a test with return redirect('post-detail', 18). At the moment once a valid post had been made then the post with ID 18 is loaded.
I am trying to get the ID of the newly created post. What I am trying to write is return redirect('post-detail', id of newly created post)
As this line works form.instance.author = self.request.user, I tried form.instance.id
but it didn't have the desired results.
Does anyone have any suggestions?
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return redirect('post-detail', 18)
#print (form.instance.id)
return redirect('post-detail', form.instance.id)
You did not save the form, hence that means that at that point in time the object has no primary key yet. Normally the form is saved in the basic form_valid method.
You furthermore probably better override get_success_url, since that is the place where you are supposed to "calculate" the url to redirect to:
from django.urls import reverse
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'content']
def get_success_url(self):
return reverse('post-detail', args=[self.object.pk])
def form_valid(self, form):
form.instance.author = self.request.user
# will save the form and redirect to the success_url
return super().form_valid(form)
Related
I want to create article form in Django. The code below getting Error FileModel.post" must be a "Article" instance. because i want to create multiple file related article instances which is coming from forgnekey. i need to pass example post=form.instances when i submit form data but i don't understand how can i pass FileModel.objects.create(post=self.form_class, file=f)? I would be grateful for any help.
views.py
class CreateView(CreateView):
form_class = FileForm
template_name = 'create.html'
success_url = '/'
success_message = "New story has been created successfully"
def form_valid(self, form):
form.instance.author = self.request.user
for f in self.request.FILES.getlist('file'):
FileModel.objects.create(post=self.form_class, file=f) # Error is here!
return super().form_valid(form)
You need to pass the instance created by the form, rather than just the form class. For example:
FileModel.objects.create(post=form.instance, file=f)
Update: As the Article hasn't been saved yet, when you try to attach it to the FileModel object, it won't let you.
Try something like this:
def form_valid(self, form):
post = form.save(commit=False)
post.author = self.request.user
post.save()
for f in self.request.FILES.getlist('file'):
FileModel.objects.create(post=post, file=f)
return redirect('/')
I am working on a car rental website for uber drivers in django, from the detailView I need drivers to be able to choose the duration of their rental, and other information will be auto filled to the form from my views.py, i was able to get the driver through request.user, i also need the PK of the car to be rented. searching through here i’ve tried various suggestions by people here, but i keep getting one error after another…
using
self.kwargs['pk'] results in ValueError at /car/offer/4/ Cannot assign "4": "CarRent.car" must be a "Car" instance.
then i tried using
form.car = Car.objects.get(pk= self.kwargs.get('pk')) which results in a AttributeError at /car/offer/4/ 'CarRent' object has no attribute 'is_valid'
can someone please tell me how to get the car instance saved in the CarRent model? any help will be greatly appreciated. Thanks
below is my code (reduced to the relevant bit)
models.py
class Car(models.Model):
car_owner = models.ForeignKey(User, related_name='car_owner', on_delete=models.CASCADE)
class CarRent(models.Model):
car = models.ForeignKey(Car, related_name='rented_car', on_delete=models.CASCADE)
driver = models.ForeignKey(User, related_name='driver_renting', on_delete=models.CASCADE)
rented_weeks = models.BigIntegerField(default=1, choices=WEEK_CHOICES)
forms.py
class RentForm(forms.ModelForm):
class Meta:
model = CarRent
fields = ['rented_weeks']
i’m only displaying the rented weeks as that’s the only information i need from the user.
views.py
class CarView(FormMixin, DetailView):
model = Car
form_class = RentForm
def get_success_url(self):
return reverse('car-details', kwargs={'pk': self.object.pk})
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
form = self.get_form()
form = form.save(commit=False)
form.car = self.kwargs['pk']
form.driver = request.user
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)
form.car expects a Car object, not a string with the primary key, but you can simply use:
from django.contrib.auth.mixins import LoginRequiredMixin
class CarView(LoginRequiredMixin, FormMixin, DetailView):
# …
def post(self, request, *args, **kwargs):
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 form_valid(self, form):
form.instance.car_id = self.kwargs['pk']
form.instance.driver = self.request.user
form.save()
return super().form_valid(form)
Note: You can limit views to a class-based view to authenticated users with the
LoginRequiredMixin mixin [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.
I'm using a custom CreateView (CourseCreate) and UpdateView (CourseUpdate) to save and update a Course. I want to take an action when the Course is saved. I will create a new many-to-many relationship between the instructor of the new course and the user (if it doesn't already exist).
So, I want to save the Course as course, and then use course.faculty to create that new relationship. Where is the best place to make this happen?
I'm trying to do this in form_valid in the views, but I'm getting errors when trying to access form.instance.faculty bc the course isn't created yet (in CourseCreate). The error message is like:
"Course: ..." needs to have a value for field "course" before this many-to-many relationship can be used.
It's also not working in CourseUpdate. The Assists relationship is not created. Should I be trying this in the Form? But I'm not sure how to get the user info to the Form.
Thank you.
models.py
class Faculty(models.Model):
last_name = models.CharField(max_length=20)
class Course(models.Model):
class_title = models.CharField(max_length=120)
faculty = models.ManyToManyField(Faculty)
class UserProfile(models.Model):
user = models.OneToOneField(User)
faculty = models.ManyToManyField(Faculty, through='Assists')
class Assists(models.Model):
user = models.ForeignKey(UserProfile)
faculty = models.ForeignKey(Faculty)
views.py
class CourseCreate(CreateView):
model = Course
template_name = 'mcadb/course_form.html'
form_class = CourseForm
def form_valid(self, form):
my_course = form.instance
for f in my_course.faculty.all():
a, created = Assists.objects.get_or_create(user=self.request.user.userprofile, faculty=f)
return super(CourseCreate, self).form_valid(form)
class CourseUpdate(UpdateView):
model = Course
form_class = CourseForm
def form_valid(self, form):
my_course = form.instance
for f in my_course.faculty.all():
a, created = Assists.objects.get_or_create(user=self.request.user.userprofile, faculty=f)
return super(CourseUpdate, self).form_valid(form)
The form_valid() method for CreateView and UpdateView saves the form, then redirects to the success url. It's not possible to do return super(), because you want to do stuff in between the object being saved and the redirect.
The first option is to not call super(), and duplicate the two lines in your view. The advantage of this is that it's very clear what is going on.
def form_valid(self, form):
self.object = form.save()
# do something with self.object
# remember the import: from django.http import HttpResponseRedirect
return HttpResponseRedirect(self.get_success_url())
The second option is to continue to call super(), but don't return the response until after you have updated the relationship. The advantage of this is that you are not duplicating the code in super(), but the disadvantage is that it's not as clear what's going on, unless you are familiar with what super() does.
def form_valid(self, form):
response = super(CourseCreate, self).form_valid(form)
# do something with self.object
return response
I would suggest to use Django's Signal. That is an action that gets triggered when something happens to a Model, like save or update. This way your code stays clean (no business logic in the form-handling), and you are sure that it only gets triggered after save.
#views.py
from django.dispatch import receiver
...
#receiver(post_save, sender=Course)
def post_save_course_dosomething(sender,instance, **kwargs):
the_faculty = instance.faculty
#...etc
If you need to modify also the Course object when call save function use False and after change save the object
def form_valid(self, form):
self.object = form.save(False)
# make change at the object
self.object.save()
return HttpResponseRedirect(self.get_success_url())
It is possible to do return super() as it is in the django doc:
https://docs.djangoproject.com/en/4.0/topics/class-based-views/generic-editing/
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
form.send_email()
return super().form_valid(form)
This is my code, i have read the documentations and it seems this method is the right way, i get no errors but i see no results. Can somebody help me in what i am doing wrong?
class BookCreate(LoginRequiredMixin, CreateView):
model = Book
fields = ['title', 'isbn', 'year', 'author', 'publisher']
def form_valid(self, form):
form.instance.owner = self.request.user
return super(BookCreate, self).form_valid(form)
def form_valid(self, form):
b = Book.objects.all
form.instance.author = ModelChoiceField(queryset=b.author_set.filter(owner=self.request.user))
return super(BookCreate, self).form_valid(form)
It is much easier to simply exclude the author from the list of fields, then set it in the form_valid method:
class BookCreate(LoginRequiredMixin, CreateView):
model = Book
fields = ['title', 'isbn', 'year', 'publisher']
def form_valid(self, form):
form.instance.owner = self.request.user
return super(BookCreate, self).form_valid(form)
If you do this, make sure you delete your second form_valid method, which is replacing the correct form_valid method above.
If you must include author as a field with a single option, then the code is much more complicated. You need a custom form with an __init__ method which takes user and sets the queryset for the auth field.
Then you need to modify your view to use your custom form, and override get_form_kwargs to include self.request.user.
I'm getting an error:
AttributeError at /courses/create/
'CourseStudentForm' object has no attribute 'user'
When I try to create a new object by setting it's user field to the current user:
class CourseStudentCreate(CreateView):
model = CourseStudent
fields = ['semester', 'block', 'course', 'grade']
success_url = reverse_lazy('quests:quests')
#method_decorator(login_required)
def form_valid(self, form):
data = form.save(commit=False)
data.user = self.request.user
data.save()
return super(CourseStudentCreate, self).form_valid(form)
This is the model:
class CourseStudent(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
semester = models.ForeignKey(Semester)
block = models.ForeignKey(Block)
course = models.ForeignKey(Course)
grade = models.PositiveIntegerField()
active = models.BooleanField(default=True)
The form displays correctly, but when I submit I get the error.
ANSWER:
From here:
Pass current user to initial for CreateView in Django
If I want to keep user as a required field, it works if I change form_valid to:
def form_valid(self, form):
form.instance.user = self.request.user
return super(CourseStudentCreate, self).form_valid(form)
The cause of the error is described by Burhan Khalid below.
The reason it doesn't work is because you are missing a required field from your form class; recall that model form validation will also validate the model instance:
Validation on a ModelForm
There are two main steps involved in validating a ModelForm:
Validating the form
Validating the model instance
In your class, the inherited post method is calling is_valid():
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance with the passed
POST variables and then checked for validity.
"""
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
You can see that it only calls form_valid() if is_valid() returns true; in your case it can't return true because you have a required attribute missing.
You can solve this problem easily by making the user foreign key optional in your model.