Django ModelForms __init__ kwargs create and update - django

I'm trying to get the request.user into a ModelForm. I feel like I've tried all permutations of this from overloading the
__init__
argument (per Django form, request.post and initial) to trying to pass it as a kwargs (per Django form __init__() got multiple values for keyword argument). I
It does seem like the kwargs is the best approach but I'm totally stymied by it.
Here's the ModelForm:
class DistListForm(ModelForm):
members = ModelMultipleChoiceField(queryset=Company.objects.none())
class Meta:
model = DistList
fields = ['name', 'description', 'members', 'is_private']
def __init__(self, *args, **kwargs):
super(DistListForm, self).__init__(*args, **kwargs)
user = kwargs.pop('user', None)
up = UserProfile.objects.get(user=user)
/.. etc ../
Here's how the create function currently works:
def distlistcreate(request):
user = {'user': request.user}
form = DistListForm(**user)
if request.method == 'POST':
form = DistListForm(request.POST)
if form.is_valid():
distlist = form.save(commit=False)
distlist.creator = request.user
distlist.save()
form.save_m2m()
return HttpResponseRedirect(reverse('distlistsmy'))
return render(request, 'distlistcreate.html',{'form':form})
which throws a TypeError: init() got an unexpected keyword argument 'user'. The update method is equally unhelpful:
def distlistupdate(request, object_id):
distlist = get_object_or_404(DistList, id=object_id)
form = DistListForm(user=request.user, instance=distlist)
if request.method == 'POST':
form = DistListForm(request.POST, user=request.user)
It also throws the same error.
I've been banging my head against this wall for two hours now. What is the correct way to pass a keyword argument into a ModelForm?
This is Django 1.6.1 if that makes a difference.

You have to pop the user argument before call super() so it will no conflict wit the default arguments of ModelForm
class DistListForm(ModelForm):
members = ModelMultipleChoiceField(queryset=Company.objects.none())
class Meta:
model = DistList
fields = ['name', 'description', 'members', 'is_private']
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(DistListForm, self).__init__(*args, **kwargs)
user_profile = UserProfile.objects.get(user=user)

Just did exactly this yesterday, on Django 1.5, and I am able to do:
def __init__(self, user, *args, **kwargs):
on my ModelForm. Then I just use user without having to pop it from the kwargs.

Related

django unable to pass request to form while using formset. Produces `__init__() got an unexpected keyword argument 'request'`

#forms.py
class RequestModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super(RequestModelForm, self).__init__(*args, **kwargs)
class BusinessForm(RequestModelForm):
class Meta:
model = Business
fields = ('title', 'stitle', 'gstin', 'address')
def clean_gstin(self):
user = self.request.user
gstin = self.cleaned_data['gstin'].upper()
if Business.objects.filter(owner=user, gstin=gstin).exists():
raise ValidationError("A Business with that GSTIN already exists")
return gstin
#views.py
class BaseFormView(FormView):
def get_form_kwargs(self):
kwargs = super(BaseFormView, self).get_form_kwargs()
kwargs['request'] = self.request
return kwargs
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.save()
return super(BaseFormView, self).form_valid(form)
class Meta:
abstract = True
class BusinessCreateView(BaseFormView):
BusinessFormSet = formset_factory(BusinessForm, extra=3)
form_class = BusinessFormSet
success_url = '/dashboard'
template_name = "business/business_create_form.html"
Everything works well if I set extra=1 for the formset_factory. But when extra = 2 or something greater than 1, the error is thrown: __init__() got an unexpected keyword argument 'request'
I have identified the problem here. Since I'm popping request inside the __init__ function of RequestModelForm(which is called multiple times when extra is set to more than 1). And there is no request since it is already been popped.
I tried doing self.request = kwargs.get("request") instead of self.request = kwargs.pop("request"). But this throws another error
'BusinessForm' object has no attribute 'request'.
How to overcome this? Any Help?
You would need to pass the extra kwarg request to the form, but the BusinessFormSet needs to have this information explicitly. Overwriting the get_form_kwargs method in the FormView doesn't work in the way you've described because it'll pass all these as kwargs to what you have defined as form_class.
See https://docs.djangoproject.com/en/2.1/topics/forms/formsets/#passing-custom-parameters-to-formset-forms
For example probably what would work is:
class BaseFormView(FormView):
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['form_kwargs'] = {'request': self.request}
return kwargs
...

Initial data for Django Admin inline formset is not saved

I'm trying to pre-fill some inlines in Django Admin with data passed as query params (in case of adding a new object in DB).
class TestCaseInlineFormSet(BaseInlineFormSet):
class Meta:
model = TestCase
fields = '__all__'
def __init__(self, *args, **kwargs):
super(TestCaseInlineFormSet, self).__init__(*args, **kwargs)
ids_string = self.request.GET.get('ids')
if ids_string:
ids = [int(x) for x in ids_string.split(',')]
self.initial = [{'test_case': id} for id in ids]
class TestCaseInline(admin.TabularInline):
model = TestCase
raw_id_fields = ('test_case',)
extra = 1
formset = TestCaseInlineFormSet
def get_formset(self, request, obj=None, **kwargs):
formset = super(TestCaseInline, self).get_formset(request, obj, **kwargs)
formset.request = request
return formset
def get_extra(self, request, obj=None, **kwargs):
extra = super(TestCaseInline, self).get_extra(request, obj, **kwargs)
requested_extras = len(request.GET.get('ids', '').split(','))
return max(extra, requested_extras)
The data is pre-filled fine with this solution, however there's an issue when trying to submit: the pre-filled inlines are not marked as changed, so they're not saved.
I've tried overriding has_changed() on the TestCaseInlineFormSet however it doesn't solve the problem - it seems has_changed() for the formset is never called?
Any idea how to fix this?

Adding custom parameter to django modelform

I'm having troubles adding a custom parameter to a modelform (in this case, the currrent user from request.user)
I used to do this on previous projects, and I can't manage to make it work on a new project on Django 1.10.4.
I got this error :
TypeError at /realestateprogram/edition_appartement/19/
__init__() got an unexpected keyword argument 'user'
This is my view :
apartment = get_object_or_404(Apartment, pk=apartment_id)
if request.method == 'POST':
form = ApartmentForm(request.POST, request.FILES, instance=apartment, user=request.user)
if form.is_valid():
form.save()
return redirect('list_realestateprogram_apartment', realestateprogram_id=apartment.realestateprogram.id)
else:
print form.errors
else:
form = ApartmentForm(instance=apartment, user=request.user)
and this is my form :
class ApartmentForm(forms.ModelForm):
class Meta:
model = Apartment
exclude = ('realestateprogram',)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(ApartmentForm, self).__init__(*args, **kwargs)
Thanks in advance if you can help me
Try to pass all the values with keyword arguments:
form = ApartmentForm(data=request.POST, files=request.FILES, instance=apartment, user=request.user)
If you look at the class based generic views, that's how they do it. I would also suggest that you look at Form handling with class-based views.

Django: Current User Id for ModelForm Admin

I want for filter a ModelChoiceField with the current user. I found a solution very close that I want to do, but I dont understand
Django: How to get current user in admin forms
The answer accepted says
"I can now access the current user in my forms.ModelForm by accessing self.current_user"
--admin.py
class Customer(BaseAdmin):
form = CustomerForm
def get_form(self, request,obj=None,**kwargs):
form = super(Customer, self).get_form(request, **kwargs)
form.current_user = request.user
return form
--forms.py
class CustomerForm(forms.ModelForm):
default_tax = forms.ModelChoiceField(queryset=fa_tax_rates.objects.filter(tenant=????))
class Meta:
model = fa_customers
How do I get the current user on modelchoice queryset(tenant=????)
How do I call the self.current_user in the modelform(forms.py)
Override __init__ constructor of the CustomerForm:
class CustomerForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.fields['default_tax'].queryset =
fa_tax_rates.objects.filter(tenant=self.current_user))
Queryset in the form field definition can be safely set to all() or none():
class CustomerForm(forms.ModelForm):
default_tax = forms.ModelChoiceField(queryset=fa_tax_rates.objects.none())
Just to sum up the solution because it was very hard for me to make this work and understand the accepted answer
In admin.py
class MyModelForm (forms.ModelForm):
def __init__(self, *args,**kwargs):
super (MyModelForm ,self).__init__(*args,**kwargs)
#retrieve current_user from MyModelAdmin
self.fields['my_model_field'].queryset = Staff.objects.all().filter(person_name = self.current_user)
#The person name in the database must be the same as in Django User, otherwise use something like person_name__contains
class MyModelAdmin(admin.ModelAdmin):
form = MyModelForm
def get_form(self, request, *args, **kwargs):
form = super(MyModelAdmin, self).get_form(request, *args, **kwargs)
form.current_user = request.user #get current user only accessible in MyModelAdminand pass it to MyModelForm
return form

ModelMultipleChoiceField passing request.user

I want to use forms.ModelMultipleChoiceField in a form. I know it takes a queryset, however the query set I will be using take the param user which I normally pass in a view using request.user. However this is in a form, how do I pass request.user? do I need to?
Entry.objects.filter(request.user)
You should override your form's init method:
class MyForm(forms.ModelForm):
class Meta:
model = Entry
def __init__(self, user=None, *args, **kwargs):
super(MyForm, self).__init__(*args,**kwargs)
if user is not None:
form_choices = Entry.objects.filter(user)
else:
form_choices = Entry.objects.all()
self.fields['my_mfield'] = forms.ModelMultipleChoiceField(
queryset=form_choices
)
and in your views, when it's time to instantiate the form:
form = MyForm(request.user)
or
form = MyForm()