Error when trying to assign a group to a user - python

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)

Related

Django form_valid error. Can anyone find the error?

I am trying to use the value from the URL on CreateView
My models are like that: Categoria > Serie
I have made a URL that path('nova-serie/<categoria>', NovaSerie.as_view(), name='nova_serie'),
The URL to create a new Serie is like that: /nova-serie/3
I am trying to use form_valid but I am receiving this message:
Cannot assign "'3'": "Serie.categoria" must be a "Categoria" instance.
views.py
class NovaSerie(CreateView):
model = Serie
form_class = SerieForm
template_name = 'nova_serie.html'
success_url = reverse_lazy('home')
def form_valid(self, form):
url = self.request.path_info
parte_final_url = url.replace('/nova-serie/', '')
form.instance.categoria = parte_final_url
return super(NovaSerie).form_valid(form)
forms.py
class SerieForm(forms.ModelForm):
class Meta:
model = Serie
fields = (
'serie',
)
widgets = {
'title': forms.TextInput(), # attrs={class="title"}
}
Can anyone here give me a help?
There is no need to do string processing on the path. You can obtain the URL parameters with self.kwargs. Furthermore if you want to specify the id of the .categoria, you should set .categoria_id:
class NovaSerie(CreateView):
model = Serie
form_class = SerieForm
template_name = 'nova_serie.html'
success_url = reverse_lazy('home')
def form_valid(self, form):
form.instance.categoria_id = self.kwargs['categoria']
return super().form_valid(form)
I would furthermore advise to specify the categoria URL parameter as an int:
path('nova-serie/<int:categoria>', NovaSerie.as_view(), name='nova_serie'),
that way if the value is not an integer it will not fire the view.

Django signal is sending two times

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)

How to access form data in `FormView.get_success_url()`

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)

Django limit the choices of a many to many relationship

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.

'__proxy__' object has no attribute 'get' in CreateView

So I'm thinking that this is not the right way to do things, but I am trying to learn django and I am trying some things out. I am trying to set a foreign key for my Formula model, by hardcoding in an instance of maker.
Models:
class Cooker(models.Model):
name = models.CharField(max_length=20, name="name")
background = models.CharField(max_length=500, name="background")
class Formula(models.Model):
food = models.CharField(max_length=200, name="food")
maker = models.ForeignKey(Cooker, related_name="cooker_key")
Views
class CookerCreate(CreateView):
template_name = "cookercreate.html"
model = Cooker
fields = ['name','background']
success_url = reverse_lazy('cooker')
class FormulaCreate(CreateView):
template_name = "formulahome.html"
model = Formula
fields = ['food']
success_url = reverse_lazy('formulahome')
def form_valid(self, form):
self.object = form.save(commit = False)
self.object.maker = Cooker.objects.get(pk=1)
form.save()
return reverse_lazy('formula home')
In the FormulaCreate class where I am setting self.object.maker, I just want to hard code in a Cooker that I already created. Thanks
EDIT: When I try to submit the form in my FormulaCreate(CreateView) I get the error Exception Value: '__proxy__' object has no attribute 'get'
The reason for your error is that form_valid should return a Response object, and you are returning a URL.
Rather than do this manually you should just call the parent method which will redirect to the success_url that you have already defined:
def form_valid(self, form):
self.object = form.save(commit = False)
self.object.maker = Cooker.objects.get(pk=1)
form.save()
return super(FormulaCreate, self).form_valid(form)
If you are using the post method return redirect('formula home') works too.

Categories