as shown in the following code i am trying to save the formset once it is validated. but i always get object has no attribute 'save'error. its great if someone can help me to use FormSetView to save and update data using forms.
from extra_views import FormSetView
from foo.forms import MyForm
class MyFormSetView(FormSetView):
template_name = 'myformset.html'
form_class = MyForm
success_url = 'success/'
def formset_valid(self, formset):
formset.save()
return super(MyFormSetView, self).formset_valid(formset)
You should use ModelFormSetView and then override your widget:
class MyForm(forms.ModelForm):
model = customer
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['birthday'].widget = birthdaySelector()
class MyFormSetView(ModelFormSetView):
template_name = 'myformset.html'
model = customer
success_url = 'success/'
form_class = MyForm
No need to define formset_valid, ModelFormSetView saves formset automatically.
Related
Hello guys I have this update view where I am not able to validate the user(owner). How to tweak this to add that bit too.? Please have a look at the code.
class StoreInfoView(UpdateView, LoginRequiredMixin):
model = Store
template_name = 'store/store_information.html'
form_class = StoreInfoForm
success_message = 'Updated'
success_url = reverse_lazy('store:store_home')
def get_object(self, queryset=None):
obj = Store.objects.get(id=self.kwargs['id'])
if obj.user != self.request.user:
raise PermissionDenied('You Don\'t have permission to edit!')
return obj
def get(self, *args, **kwargs):
self.object = Store.objects.get(id=self.kwargs['id'])
form_class = self.get_form_class()
form = self.get_form(form_class)
context = self.get_context_data(object=self.object, form=form)
return self.render_to_response(context)
Thanks
Instead of overriding like this, you can simply override get_queryset() method. Like this:
class StoreInfoView(UpdateView, LoginRequiredMixin):
model = Store
template_name = 'store/store_information.html'
form_class = StoreInfoForm
success_message = 'Updated'
success_url = reverse_lazy('store:store_home')
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
return queryset.filter(user=self.request.user)
In this way, non-owner users will get 404 error when they try to update.
Also, you do not need to override any other methods like get() and get_object() method.
The issue to your problem is the order of inheritance. When you will go through the official docs for LoginRequiredMixin, you will find this
This mixin should be at the leftmost position in the inheritance list.
Please update your code to this
class StoreInfoView(LoginRequiredMixin, UpdateView):
model = Store
template_name = 'store/store_information.html'
form_class = StoreInfoForm
success_message = 'Updated'
success_url = reverse_lazy('store:store_home')
...
Notice, now LoginRequiredMixin is put before the UpdateView. This should resolve your query.
I hope it helps. :)
Perhaps just another stupid beginner question:
I am having trouble overwriting a django model form which is currently using the following code in views.py.
The code below works well !
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'content']
### want this instead of line 1 and 2 above --> form_class = PostCreateForm()
view_args = collections.namedtuple('view_args', ['page_title'])
view_args = view_args(page_title="Create Post")
def get_context_data(self, *args, **kwargs):
self.tgt_url_args = MyHelper.parse_tgt_url(self)
context = super(PostCreateView, self).get_context_data(*args, **kwargs)
context.update(MyHelper.get_context_metadata(self, self.tgt_url_args, PostCreateView.view_args))
return context
def form_valid(self, form):
form.instance.author = self.request.user
tgt_url_args = MyHelper.parse_tgt_url(self)
blog_article = Article.objects.get(pk=tgt_url_args.get('Article', '0'))
form.instance.article_field = blog_article
return super().form_valid(form)
However, I would like to overwrite the form in order to add placeholder to the fields. So I replace line 1 and 2 by:
form_class = PostCreateForm()
I also added the follwing in forms.py:
from django import forms
from . models import Post
class PostCreateForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content']
print('imported PostCreateForm')
When I import and run this however, I get the following error:
'PostCreateForm' object is not callable
What am I doing wrong here?
You should not pass a constructed form, only a reference to the form class:
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
form_class = PostCreateForm # no parenthesis
# ...
Django will thus each time construct a new form (depending on the situation with request.POST, etc.).
If you need to pass extra parameters to the form, you can specify a dictionary in get_form_kwargs [Django-doc].
I would like to pass a kwarg to set a modelform field but im struggling to figure out how to do it.
My URL is as follows:
url(r'^tent/create/(?P<munc>\d+)',views.TentCreate.as_view(),name='tent_create'),
My view is simply:
class TentCreate(CreateView):
model = Tent
form_class = TentForm
And my form:
class TentForm(ModelForm):
class Meta:
model = Tent
exclude =('asfo','niho')
def __init__(self, *args, **kwargs):
super(TentForm, self).__init__(*args, **kwargs)
self.fields['primary'].queryset = Mark.objects.filter(munc=self.kwargs['munc'])
from the model:
class Tent(models.Model):
primary = models.ForeignKey(Mark,on_delete=models.CASCADE)
I can render the form fine without overriding def __init, with no filtering applied to the 'primary' field.
However attempting to use the def __init code I've described above to pass the munc kwarg to the form field is resulting in the following error:
"'TentForm' object has no attribute 'kwargs'"
I've been going around in circles trying to work through this so I would be really appreciative if anyone is able to provide me some guidance to solve this. This is my first Django project so I'm learning how I go so I assume I have made some fundamental error somewhere here!
Try overriding get_form_kwargs method:
views.py
class TentCreate(CreateView):
model = Tent
form_class = TentForm
def get_form_kwargs(self):
kwargs = super(TentCreate, self).get_form_kwargs()
kwargs.update({'munc': self.kwargs['munc']})
return kwargs
forms.py
class TentForm(ModelForm):
class Meta:
model = Tent
exclude =('asfo','niho')
def __init__(self, *args, **kwargs):
munc = kwargs.pop('munc')
super(TentForm, self).__init__(*args, **kwargs)
self.fields['primary'].queryset = Mark.objects.filter(munc=munc)
class TentCreate(CreateView):
form_class = TentForm
def get_form(self, form_class=None):
if form_class is None:
form_class = self.get_form_class()
kwargs = self.get_form_kwargs()
print(kwargs, self.kwargs)
kwargs.update(self.kwargs)
return form_class(**kwargs)
forms.py
class TentForm(ModelForm):
class Meta:
model = Tent
exclude =('asfo','niho')
def __init__(self, *args, **kwargs):
munc=self.kwargs['munc']
super(TentForm, self).__init__(*args, **kwargs)
self.fields['primary'].queryset = Mark.objects.filter(munc=munc)
you must pop munc before call super(TentForm, self).__init__(*args, **kwargs)
I want to set a dynamic variable into queryset of forms.py , I used __init__ to pass the dynamic variable , I think the code in forms.py is correct, the problem is how to pass the variable in views?
forms.py :
class ContainerForm(forms.ModelForm):
vehicle=forms.ModelChoiceField(required=False,queryset=Vehicle.objects.all(),widget=forms.Select(attrs={'class':'form-control'}))
def __init__(self, *args, **kwargs):
vehicle_id = kwargs.pop('vehicle_id',None)
super(ContainerForm, self).__init__(*args, **kwargs)
if vehicle_id:
self.fields['vehicle'].queryset = Vehicle.objects.filter(id=vehicle_id)
views.py
class ContainerCreate(CreateView):
form_class = ContainerForm(id= vehicle_id)
template_name = 'vehicule_app/container_form.html'
the error said :
Exception Value:'ContainerForm' object is not callable
If you want to use the vehicle_id from the URL, then you can exclude the field from the model form:
class ContainerForm(forms.ModelForm):
class Meta:
model = Container
exclude = ['vehicle']
You can then fetch the parameter from self.kwargs, and set the value on the form's instance in get_form_kwargs:
class ContainerCreate(CreateView):
form_class = ContainerForm
template_name = 'vehicule_app/container_form.html'
def get_form_kwargs(self):
kwargs = super(ContainerCreate, self).get_form_kwargs()
if kwargs['instance'] is None:
kwargs['instance'] = Container()
kwargs['instance'].vehicle_id = self.kwargs['pk'] # Fetch the vehicle_id from the URL
return kwargs
Note that the above code will not validate the id from the URL. The user could change it to any value they like.
If you want to keep the vehicle field in the form but with a single choice, then override the __init__ method and set the queryset.
class ContainerForm(forms.ModelForm):
class Meta:
model = Container
def __init__(self, *args, **kwargs):
vehicle_id = kwargs.pop('vehicle_id')
self.fields['vehicle'].queryset = Vehicle.objects.filter(id=vehicle_id)
Then in the get_form_kwargs method, add vehicle_id to kwargs instead:
class ContainerCreate(CreateView):
form_class = ContainerForm
template_name = 'vehicule_app/container_form.html'
def get_form_kwargs(self):
kwargs = super(ContainerCreate, self).get_form_kwargs()
kwargs['vehicle_id'] = self.kwargs['pk']
return kwargs
Hi Stackoverflow people,
In my clean function in forms.py, I would like to save automatically some information in a session variable. However, I do not seem to get access to the request variable.
All examples for handing over the request variable are based on function based views, but here I am using a class based view.
My forms.py:
from django import forms
from item.models import Item
class CreateItemForm(forms.ModelForm):
class Meta:
model = Item
fields = ('name', 'description')
def __init__(self, request, *args, **kwargs):
self.request = request
super(CreateItemForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super(CreateItemForm, self).clean()
if cleaned_data.get("address"):
self.request.session['name'] = cleaned_data.get("name")
else:
raise forms.ValidationError(_('Oops, can\'t find location.'))
return self.cleaned_data
My views.py:
from django.views.generic.edit import FormView
from item.forms import CreateItemForm
class ItemCreate(FormView):
form_class = CreateItemForm
template_name = 'item/item_create.html'
success_url = 'http://www.google.com'
What is the best way to hand over the request variable from the views.py to forms.py?
Thank you for your answer.
You can overwrite the FormMixin's get_form_kwargs method to add the request for to the form's init parameters:
class ItemCreate(FormView):
def get_form_kwargs(self):
kwargs = super(ItemCreate, self).get_form_kwargs()
kwargs.update({
'request' : self.request
})
return kwargs
Overriding the form.get_initial() works for me
class ItemCreate(FormView):
def get_initial(self):
init = super(ItemCreate, self).get_initial()
init.update({'request':self.request})
return init
And in the clean we can access it with form.initial dict
class sampleForm(forms.Form):
...
...
def clean(self):
user_request = self.initial['request']
By the way, we don't need to pop the extra args like suggested above.