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.
Related
Relevant FormView:
class addrecipe(FormView):
form_class = AddRecipeForm
model = Recipe
template_name = 'recipebook/addrecipe.html'
fields = '__all__'
extra_context = {
'recipe_list': Recipe.objects.all()
}
Relevant Form:
class AddRecipeForm(forms.ModelForm):
name = forms.CharField(max_length="50", label="Recipe Name")
description = forms.Textarea(attrs={'class': 'desc-text-area'})
servings = forms.IntegerField()
tools = forms.ModelMultipleChoiceField(queryset=Tool.objects.all(), widget=forms.CheckboxSelectMultiple, required = True, help_text="Select all relevant tools")
class Meta:
model = Recipe
fields = ("__all__")
URL pattern for the details view page:
path('<int:pk>/recipedetails', views.recipedetails.as_view(), name='recipe_details'),
I want to have the user submit the form, then be taken to the details page of the entry they just made into the database. I've tried doing this using reverse/reverse_lazy with a success url but that hasn't been successful.
I also tried adding the following to my form view class:
def get_success_url(self):
test_recipe_id = self.object.id
return reverse('recipeBook:recipe_details', pk=test_recipe_id)
After also changing my path to:
re_path(r'(?P<pk>[^/]+)/recipedetails', views.recipedetails.as_view(), name='recipe_details'),
I get the following Value error:
AttributeError at /recipebook/addrecipe
'addrecipe' object has no attribute 'object'
Your solution was almost there.
You could use the get_success_url method to get the recipe ID after the model. This will allow you redirect with parameters.
class addrecipe(FormView):
form_class = AddRecipeForm
model = Recipe
template_name = 'recipebook/addrecipe.html'
fields = '__all__'
extra_context = {
'recipe_list': Recipe.objects.all()
}
#New method
def get_success_url(self):
test_recipe_id = self.object.id #gets id from created object
return reverse('recipeBook:recipe_details', pk=test_recipe_id)
Your detail url is not receiving the parameter as expected hence it needs to be reconfigured with a new regex
Old:
path('<int:pk>/recipedetails', views.recipedetails.as_view(), name='recipe_details'),
New:
from django.urls import path, re_path
re_path(r'(?P<pk>[^/]+)/recipedetails', views.recipedetails.as_view(), name='recipe_details),
I needed to use HttpResponseRedirect to redirect correctly. My view ended up looking like this:
class addrecipe(FormView):
form_class = AddRecipeForm
model = Recipe
template_name = 'recipebook/addrecipe.html'
fields = '__all__'
extra_context = {
'recipe_list': Recipe.objects.all()
}
def form_valid(self, form):
test_recipe = form.save(commit=False)
test_recipe.save()
test_recipe_id = test_recipe.id
return HttpResponseRedirect(reverse('recipeBook:recipe_details', kwargs={'pk': test_recipe_id}))
Saving the object before grabbing the ID appears to be a necessary step as I found that the ID itself is only created when the object is created.
The reverse return wasn't working, so honestly I hail mary'd a httpresponseredirect in front and it worked. I will update the answer if I figure out why..
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)
I have a todo APP and I would like to have a link where the user can click and set the "todo" as complete without deleting it from my database.
I use CBV but cannot figure out how do it :
I tried
views.py :
class TodoDeleteView(LoginRequiredMixin, DeleteView):
model = Todo
success_url = '/'
template_name = 'dashboard/dashboard_confirm_delete.html'
def completeTodo(request, todo_id):
todo = Todo.objects.get(pk=todo_id)
todo.complete = True
todo.save()
But it delete it from my DB and it does not set it to true.
My models.py
class Todo(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE,verbose_name="Nom de l'utilisateur")
text = models.CharField(max_length=150, verbose_name="Nom de la Todo")
complete = models.BooleanField(default=False)
You define a DeleteView, and deleting the object, is just part of the delete control flow. In order to change the behavior, we can override the delete function, like:
class TodoDeleteView(LoginRequiredMixin, DeleteView):
model = Todo
pk_url_kwarg = 'todo_id'
success_url = '/'
template_name = 'dashboard/dashboard_confirm_delete.html'
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
success_url = self.get_success_url()
self.object.complete = True
self.object.save()
return HttpResponseRedirect(success_url)
The pk_url_kwarg is necessary to use this to obtain the primary key to filter and retrieve the correct object with get_object().
The reason why we might want to use a DeleteView, is because people can make a DELETE request on that endpoint.
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.
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.