I'm trying to use Django signals in my application but the post_save signal is sending two times, and the post_delete is sending once like it's supposed to.
My signals.py:
#receiver([post_save,post_delete], sender=Award)
def print_request(sender, **kwargs):
print('Request finished!')
The result in my terminal when I save an object:
Request finished!
Request finished!
My CreateView:
class ResearcherPrizeCreateView(LoginRequiredMixin, CreateView):
model = Award
form_class = ResearcherPrizeCreateForm
success_url = reverse_lazy('prize_list')
template_name = 'researcher/award/award_create_form.html'
def form_valid(self, form):
researcher = Researcher.objects.get(user_id=self.request.user.id)
self.object = form.save(commit=False)
self.object.researcher = researcher
self.object.save()
return super(ResearcherPrizeCreateView, self).form_valid(form)
My form.py
class ResearcherPrizeCreateForm(forms.ModelForm):
class Meta:
model = Award
fields = ('title', 'category')
widgets = {
'title':forms.TextInput(attrs={
'class':'form-control',
'placeholder':'Titulo da Premiação'
}),
'category':forms.Select(attrs={
'class':'form-control',
})
}
The object is being saved once when you call self.object.save(), and again when you call super().form_valid(form).
One option is to redirect in your form_valid method.
def form_valid(self, form):
researcher = Researcher.objects.get(user_id=self.request.user.id)
self.object = form.save(commit=False)
self.object.researcher = researcher
self.object.save()
return redirect(self.success_url)
Another option is to assign the researcher to the form instance then call super(...).form_valid() to save the form and redirect.
def form_valid(self, form):
researcher = Researcher.objects.get(user_id=self.request.user.id)
form.instance.researcher = researcher
return super(ResearcherPrizeCreateView, self).form_valid(form)
Related
I have a detailed user view that has a button for user updates. The user update form is inside a modal, for that, I am using a FormView ModelForm and a TbUser. I don't get how the form validation works but the fields are correct. When I update something for a user, I get an error, TbUser with username already exists, which means the code does not update the user but tries to add a new one. Also, I want to redirect to user-detail page after submit.
views.py
class UserUpdateView(LoginRequiredMixin, SuccessMessageMixin, FormView):
form_class = UserUpdateForm
template_name = 'users/modals/user_update_modal.html'
success_message = "User updated successfully."
def get_form_kwargs(self):
kw = super().get_form_kwargs()
kw['request'] = self.request
return kw
def form_valid(self, form):
obj = form.save(commit=False)
print(obj.username)
print('valid')
TbUser.objects.filter(id=self.request.user.id).update(username=obj.username, real_name=obj.real_name,
email=obj.email, cellphone=obj.cellphone,
department=obj.department, role=obj.role)
def form_invalid(self, form):
messages.error(self.request, form.errors)
# Where to redirect here? I want to
def get_success_url(self):
return reverse('user-detail', kwargs={'pk': self.formclass})
forms.py
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
def __init__(self, request, *args, **kwargs):
super().__init__(*args, **kwargs)
self.request = request
if request.user.customer:
self.fields['department'].queryset = TbDepartment.objects.filter(
customer=request.user.customer)
self.fields['role'].queryset = TbRole.objects.filter(
customer=request.user.customer)
class Meta:
model = TbUser
fields = ['username', 'real_name', 'email',
'cellphone', 'department', 'role']
urls.py
urlpatterns = [
path('users-list/', views.UsersListView.as_view(), name='users-list'),
path('user-detail/<str:pk>/',
views.UserDetailView.as_view(), name='user-detail'),
path('tb-user-update-form/<str:pk>/update/',
views.UserUpdateView.as_view(), name='tb-user-update-form'),
]
You need to override the get_object method to let it point to the object you want to update.
A FormView will construct a form *without looking for an object, you can use an UpdateView [Django-doc] to fetch the object with get_object, and then inject this in the form to update that object:
from django.views.generic import UpdateView
class UserUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
form_class = UserUpdateForm
template_name = 'users/modals/user_update_modal.html'
success_message = "User updated successfully."
def get_form_kwargs(self):
kw = super().get_form_kwargs()
kw['request'] = self.request
return kw
def get_object(self, *args, **kwargs):
return self.request.user
def form_invalid(self, form):
messages.error(self.request, form.errors)
return super().form_invalid(form)
def get_success_url(self):
return reverse('user-detail', kwargs={'pk': self.object.pk })
I am trying to assign a group to a user but without the need to use the django manager, but I run into a problem and it tells me that "<User: island>" must have a value for the field "id" before this many-to-many relationship can be used.
This is my view:
class UserCreationView(LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView):
permission_required = 'users.add_user'
login_url = 'users:login'
template_name = 'users/register.html'
model = User
form_class = UserForm
success_message = 'El usuario fue creado exitosamente'
success_url = reverse_lazy('users:user')
def form_valid(self, form):
self.object = form.save(commit=False)
group = Group.objects.get(pk=self.request.POST.get('groups'))
self.object.groups.add(group)
self.object.save()
Simply rearrange order of statements
self.object = self.object.save()
self.object.groups.add(group)
Also there is no need to add instance to object you can just use normal variable instead, and your parameter would be better called group/group_id instead of groups
def form_valid(self, form):
user = form.save()
group = Group.objects.get(pk=self.request.POST.get('group_id'))
user.groups.add(group)
Currently, I have a form that looks like this:
class SelectBoard(generic.FormView):
form_class = forms.SelectBoard
def form_valid(self, form):
board_name = form.cleaned_data['name']
return redirect('leaderboard', board_name=board_name)
However, it would be my preference to do the more idiomatic thing, and use get_success_url. Something like this:
class SelectBoard(generic.FormView):
form_class = forms.SelectBoard
def get_success_url(self):
form = ???
board_name = form.cleaned_data['name']
return reverse('leaderboard', board_name=board_name)
However, the form is not passed to get_success_url, and unlike many other pieces of request context (like self.request, self.kwargs, or self.object (in DetailView)), the form is not attached as an attribute at any point in the standard FormView dispatch sequence. Is there any good way to access cleaned & validated form data in get_success_url (that is, without having to access self.request.POST or reconstruct the form from scratch)?
You can override form_valid method to write form as instance attribute and use self.form inside get_success_url:
class SelectBoard(generic.FormView):
form_class = forms.SelectBoard
def form_valid(self, form):
"""If the form is valid, redirect to the supplied URL."""
self.form = form
return HttpResponseRedirect(self.get_success_url())
def get_success_url(self):
board_name = self.form.cleaned_data['name']
return reverse('leaderboard', board_name=board_name)
You might set the success_url attribute in form_valid method getting the form field value if it is needed
class SelectBoard(generic.FormView):
form_class = forms.SelectBoard
def form_valid(self, form):
board_name = form.cleaned_data['name']
self.success_url = reverse('leaderboard', board_name=board_name)
return super().form_valid(form)
I know the title says the question has been asked before but the situation is different.
I have something called Agent:
class Agent(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='agents')
...
and a Group:
class Group(models.Model):
agents = models.ManyToManyField('agents.Agent', blank=True, related_name='groups')
now with Django class based views (UpdateView maybe) I want create a view that a user can see only its agents and select only one of them to add it to a specific group.
as far as I get was this
#method_decorator(login_required, name='dispatch')
class GroupAgentRegister(UpdateView):
model = Group
fields = ('agents',)
template_name = 'register.html'
context_object_name = 'group'
def get_form(self, form_class=None):
form = super(GroupAgentRegister, self).get_form(form_class)
form.fields['agents'].queryset = self.request.user.agents.all()
return form
def form_valid(self, form):
if self.object.agents.filter(user=self.request.user):
form.add_error(None, ValidationError(u'Already Registered'))
return super(GroupAgentRegister, self).form_invalid(form)
return super(GroupAgentRegister, self).form_valid(form)
the form rendering is fine except that I'm able to select multiple agents.
but when I select a value and post it it replace the new selected agents with existing ones and it's not appended to the old ones.
I solved it this way. it may help others too.
first I created a form:
class GroupRegistrationForm(forms.ModelForm):
agents = forms.ModelChoiceField(Group.objects.none())
class Meta:
model = Group
fields = ('agents',)
and I changed the register view to this:
#method_decorator(login_required, name='dispatch')
class GroupAgentRegister(UpdateView):
model = Group
form_class = GroupRegistrationForm
fields = ('agents',)
template_name = 'register.html'
context_object_name = 'group'
def get_form(self, form_class=None):
form = super(GroupAgentRegister, self).get_form(form_class)
form.fields['agents'].queryset = self.request.user.agents.all()
return form
def form_valid(self, form):
if self.object.agents.filter(user=self.request.user):
form.add_error(None, ValidationError(u'Already Registered'))
return super(GroupAgentRegister, self).form_invalid(form)
self.object.agents.add(form.cleaned_data['agents'])
self.object.save()
return HttpResponseRedirect(self.get_success_url())
and everything works fine with the most minimal change I had to apply.
I have this model:
class Post(models.Model):
thread = models.ForeignKey(Thread)
post_title = models.CharField(max_length=50, blank=True)
# other attributes
And I have a view:
class ThreadView(CreateView):
model = models.Post
template_name = 'forum/thread.html'
fields = ['post_title', 'author', 'post_text']
When I try to send the form I get IntegrityError: NOT NULL constraint failed: forum_post.thread_id.
I think, it's because I foreign key remains empty, but I don't know how to add it automatically.
First, the name of the view you have is not quiet obvious, cause you are trying to create an instance of a Post not of a Thread. Won't it be better to rename it to PostCreateView?
Speaking about the error you get, you are right about foreign key - it is empty. After all, you do not set it anywhere. You should either send it in the form or assign it on validation. The second way is what you are looking for:
class ThreadView(CreateView):
model = models.Post
template_name = 'forum/thread.html'
fields = ['post_title', 'author', 'post_text']
def dispatch(self, *args, **kwargs):
self.thread = get_object_or_404(Thread, pk=kwargs['thread_id'])
return super(ThreadView, self).dispatch(*args, **kwargs)
def form_valid(self, form):
form.instance.thread = self.thread
return super(ThreadView, self).form_valid(form)
I think you must add ForeginKey Feild into Views Feilds
fields = ['thread', 'post_title', 'author', 'post_text']
and be sure there is a data in thread model
Try adding this to your view:
def post(self, *args, **kwargs):
self.t_id = kwargs["t_id"]
return super(ThreadView, self).post(*args, **kwargs)
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.thread = Thread.objects.get(pk=self.t_id)
form.save_m2m()
return super(ModelFormMixin, self).form_valid(form)