Pass kwarg into an inlineformset_factory? - python

I am trying to pass the request object into my inlineformset_factory and am struggling to accomplish this.
In forms.py I have the following:
class SummativeScoreForm(forms.ModelForm):
"""
Form definition for SummativeScore Form
"""
subdomain_proficiency_level = forms.ModelChoiceField(
empty_label="Undecided",
queryset=SubdomainProficiencyLevel.objects.none(),
widget=forms.RadioSelect,
required=False,
)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request", None)
super(SummativeScoreForm, self).__init__(*args, **kwargs)
if self.instance:
if self.request.user == self.instance.summative.employee:
self.fields["subdomain_proficiency_level"].disabled = True
self.fields[
"subdomain_proficiency_level"
].queryset = SubdomainProficiencyLevel.objects.filter(
subdomain=self.instance.subdomain
)
self.fields[
"subdomain_proficiency_level"
].label = f"""
{self.instance.subdomain.character_code}:
{self.instance.subdomain.short_description}
"""
class Meta:
model = SummativeScore
fields = "__all__"
widgets = {
"subdomain_proficiency_level": forms.RadioSelect(
attrs={"class": "list-unstyled"}
),
}
SummativeScoreInlineFormset = inlineformset_factory(
Summative,
SummativeScore,
fields=("subdomain_proficiency_level",),
can_delete=False,
extra=0,
form=SummativeScoreForm,
)
I'm using a FormView CBV to show this inline_formset
class SummativeScoreFormView(
LoginRequiredMixin,
UserIsObserverOrObserveeMixin,
SingleObjectMixin,
FormView,
):
model = Summative
template_name = "commonground/summative_score_form.html"
pk_url_kwarg = "summative_id"
def get(self, request, *args, **kwargs):
summative_id = kwargs.pop("summative_id")
self.object = self.get_object(
queryset=Summative.objects.filter(id=summative_id)
)
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
summative_id = kwargs.pop("summative_id")
self.object = self.get_object(
queryset=Summative.objects.filter(id=summative_id)
)
return super().get(request, *args, **kwargs)
def get_form(self, form_class=None):
return SummativeScoreInlineFormset(
**self.get_form_kwargs(), instance=self.object
)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["request"] = self.request
return kwargs
def form_valid(self, form):
form.save()
messages.add_message(messages.SUCCESS, "Changes were saved!")
HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form):
print("invalid form")
return super().form_invalid(form)
def get_success_url(self):
user_id = self.kwargs["user_id"]
summative_id = self.kwargs["summative_id"]
return reverse(
"commonground:summative_detail",
kwargs={
"user_id": user_id,
"summative_id": summative_id,
},
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
summative = get_object_or_404(
Summative, pk=self.kwargs["summative_id"]
)
context["summative"] = summative
return context
I keep getting this error:
__init__() got an unexpected keyword argument 'request'
I'm not sure how best to resolve this - any ideas? Is there a clear way to pass the request to the inlineformset_factory?

You are passing the request to the formset and not to the forms in the formset, hence you get the error. For passing custom parameters to the formsets forms [Django docs] you need to pass a form_kwargs parameter to the formset:
class SummativeScoreFormView(
LoginRequiredMixin,
UserIsObserverOrObserveeMixin,
SingleObjectMixin,
FormView,
):
...
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['form_kwargs'] = {'request': self.request}
return kwargs

Related

How to detailview pk in post method in DetailView? (Django)

In my detailView I have 2 methods get_context_data and post. In get_context_data I can get the detailView pk with self.object.pk but how can I get it in the post method?
[ updated ]
here is the view
class Class_detailView(LoginRequiredMixin, DetailView):
login_url = '/'
model = Class
template_name = "attendance/content/teacher/class_detail.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['attendance_form'] = AttendanceForm(current_class_pk=self.object.pk) # pass data to form via kwargs
return context
def post(self, request, *args, **kwargs):
if request.method == "POST":
attendance_form = AttendanceForm(request.POST)
if attendance_form.is_valid():
attendance_form.instance.teacher = self.request.user
attendance_form.save()
return redirect('class-detail', pk=self.kwargs.get('pk'))
form
class AttendanceForm(forms.ModelForm):
class Meta:
model = Attendance
fields = ['student',]
def __init__(self, *args, **kwargs):
current_class_pk = kwargs.pop('current_class_pk')
super(AttendanceForm, self).__init__(*args, **kwargs)
current_student = Class.objects.get(id=current_class_pk)
self.fields['student'].queryset = current_student.student
I want to get the pk and pass it to the form when the post request is called.
How can I do it?
did you try this:
def post(self, request, *args, **kwargs):
if request.method == "POST":
attendance_form = AttendanceForm(request.POST, current_class_pk=self.kwargs.get('pk'))
if attendance_form.is_valid():
attendance_form.instance.teacher = self.request.user
attendance_form.save()
return redirect('class-detail', pk=self.kwargs.get('pk'))

Django CBV - set form class based on permissions?

I have created two forms in forms.py one form has less fields than the other.
what I would like to now do is get the current users permissions and set the form class of the CBV based on those perms.
below is my current view:
class EditCircuit(UpdateView):
model = Circuits
# if user_passes_test(lambda u: u.has_perm('config.edit_circuit')))
form_class = CircuitForm
# else
# form_class = CircuitFormRestricted
template_name = "sites/circuit_form.html"
#method_decorator(user_passes_test(lambda u: u.has_perm('config.edit_circuit')))
def dispatch(self, *args, **kwargs):
self.site_id = self.kwargs['site_id']
self.site = get_object_or_404(SiteData, pk=self.site_id)
return super(EditCircuit, self).dispatch(*args, **kwargs)
def get_success_url(self, **kwargs):
return reverse_lazy("sites:site_detail_circuits", args=(self.site_id,))
def form_valid(self, form):
form.instance.site_data = self.object.site_data
return super(EditCircuit, self).form_valid(form)
def get_form_kwargs(self, *args, **kwargs):
kwargs = super().get_form_kwargs()
return kwargs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['SiteID']=self.site_id
context['SiteName']=self.site.location
context['FormType']='Edit'
context['active_circuits']='class="active"'
return context
You can override the get_form_class method.
def get_form_class(self):
if self.request.user.has_perm('config.edit_circuit'):
return CircuitForm
return CircuitFormRestricted
Also, it looks as though you don't need the get_form_kwargs definition, as it's not doing anything at the moment.
you can create your own mixin like this
class AuthorOnlyMixin(object):
def has_permissions(self):
return self.get_object().created_by == self.request.user
then use it like this
class EditViewClass(AuthorOnlyMixin, EditView):
def get_form_class(self):
if self.has_permissions():
return FormWithPermission
else:
return FormWithoutPermission

Django CreateView didn't return an HttpResponse object

Can't figure out why CreateView doesn't return HttpResponse. For now, I use this view just for posting (no GET). I thought that set self.success_url should be enough (as you can see in def post).
class TripCreationView(CreateView):
form_class = TripCreationForm
template_name = 'frontend/homepage.html'
def post(self, request, *args, **kwargs):
self.success_url = request.POST.get('success_url') or reverse('frontend:homepage')
super(TripCreationView, self).post(self, request, *args, **kwargs)
#
# def form_valid(self, form):
# trip = form.save(self.request)
# return HttpResponseRedirect(self.success_url)
def get_form_kwargs(self):
kwargs = super(TripCreationView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
Do you know what to do?
You forgot a return statement.
def post(self, request, *args, **kwargs):
self.success_url = request.POST.get('success_url') or reverse('frontend:homepage')
return super(TripCreationView, self).post(self, request, *args, **kwargs)

Python edit form data prior to django validation

I'm fairly new to python and have been searching for awhile to find how I can edit form data BEFORE all the standard python form/field validators do their magic.
I have a model form with an IntegerField which I'd like to remove the "$" and commas from (using some sort of custom validation), then let the normal to_python() validate() etc do their thing.
My code is below - any help would be much appreciated!
forms.py
class BuyerSettingsForm(forms.ModelForm):
total_offer_limit = forms.IntegerField(required=False, max_value=10000000, min_value=0)
def __init__(self, request, *args, **kwargs):
super(BuyerSettingsForm, self).__init__(*args, **kwargs)
class Meta:
model = Buyer
fields = ['total_offer_limit']
def save(self, commit=True):
profile = super(BuyerSettingsForm, self).save(commit=commit)
profile.total_offer_limit = self.cleaned_data['total_offer_limit']
profile.save()
return profile
views.py
class SettingsPreferences(LoginRequiredMixin, BuyerAccessRequiredMixin, BuyerAdminAccessRequiredMixin, UpdateView):
template_name = 'invoicely/buyer/settings/buyer_settings.html'
form_class = BuyerSettingsForm
success_url = reverse_lazy('settings_preferences')
def get_object(self, *args, **kwargs):
return self.request.user.profile.buyer
def get_initial(self):
ctx = super(SettingsPreferences, self).get_initial()
ctx.update({
'total_offer_limit': self.object.total_offer_limit,
})
return ctx
def get_form_kwargs(self):
kwargs = super(SettingsPreferences, self).get_form_kwargs()
kwargs['request'] = self.request
return kwargs
def form_valid(self, form):
self.object = form.save()
messages.add_message(self.request, messages.SUCCESS, "Settings successfully updated")
return super(SettingsPreferences, self).form_valid(form)
If you are already overloaded get_form_kwargs you can do this. This is data which your form will be initialized with. So we can edit it before its initialization.
class SettingsPreferences(LoginRequiredMixin, BuyerAccessRequiredMixin, BuyerAdminAccessRequiredMixin, UpdateView):
...
def get_form_kwargs(self):
kwargs = super(SettingsPreferences, self).get_form_kwargs()
kwargs = copy.deepcopy(kwargs)
kwargs['request'] = self.request
if self.request.method in ('POST', 'PUT'):
# here put your data editing code
kwargs['data']['total_offer_limit'] = int(kwargs['data']['total_offer_limit'].strip().replace('$', ''))
return kwargs

Django, change username form

I'm trying to create a modelform to allow users to change their username. I'm trying to do this using FormView instead of UpdateView because I want to include other forms (user management functionality) in this view eventually.
Relevant forms.py:
class FormHorizontalModelForm(forms.ModelForm)
def __init__(self, *args, **kwargs):
super(FormHorizontalModelForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
class PlayerRenameForm(FormHorizontalModelForm):
def __init__(self, user, *args, **kwargs):
super(PlayerRenameForm, self).__init__(*args, **kwargs)
self.helper.add_input(Submit('change_username', 'Change Username'))
class Meta:
model = User
fields = ('username',)
def save(self):
pass # I figured this might be saving the object since this is inherited off ModelForm
def form_valid(self, form):
self.change_username(new_username = self.cleaned_data['username'])
# self.send_email(new_username = self.cleaned_data['username'])
def change_username(self, new_username):
player = Player.objects.get(user = self.instance)
self.instance.username = new_username
self.instance.save()
player.changed_username = True
player.save()
views.py
class AccountView(UserAuthenticationMixin, FormView):
template_name = 'game/profile.html'
success_url = '/accounts/'
form_class = PlayerRenameForm
form_class_two = CrispyPasswordSetForm
form_class_three = CrispyPasswordChangeForm
def get_context_data(self, **kwargs):
context = super(AccountView, self).get_context_data(**kwargs)
if 'form' not in context:
context['form'] = self.form_class(user = self.request.user, data = (self.request.POST or None))
if 'password_set_form' not in context:
context['password_set_form'] = self.form_class_two(user = self.request.user, data = (self.request.POST or None))
if 'password_change_form' not in context:
context['password_change_form'] = self.form_class_three(user = self.request.user, data = (self.request.POST or None))
return context
def form_invalid(self, **kwargs):
return self.render_to_response(self.get_context_data(**kwargs))
def get_form(self, form_class):
return form_class(user = self.request.user, **self.get_form_kwargs())
def post(self, request, *args, **kwargs):
if 'change_username' in request.POST:
form = self.form_class(user = request.user, instance = request.user, data = self.request.POST)
form_name = 'form'
elif 'set_password' in request.POST:
form = self.form_class_two(user = request.user, data = self.request.POST)
form_name = 'password_set_form'
elif 'change_password' in request.POST:
form = self.form_class_three(user = request.user, data = self.request.POST)
form_name = 'password_change_form'
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(**{form_name: form})
The form returns valid, and returns to success_url. I know for a fact that it is passing into the correct part of the post function since I have stripped away the other parts to ensure that was working.

Categories