Opened 9 years ago
Closed 9 years ago
#27240 closed New feature (duplicate)
Allow passing custom parameters to formset forms in admin
| Reported by: | Alexey Rogachev | Owned by: | nobody |
|---|---|---|---|
| Component: | contrib.admin | Version: | dev |
| Severity: | Normal | Keywords: | admin, form, formset, parameter |
| Cc: | Triage Stage: | Unreviewed | |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description (last modified by )
I have Enterprise model and related model Attachment. Attachments are managed using formsets in both frontend and backend.
I use custom form to handle attachments, and it uses additional user kwarg:
def __init__(self, *args, **kwargs): self.user = kwargs.pop('user', None) super(AttachmentForm, self).__init__(*args, **kwargs)
I need to fill this parameter with request.user value. In frontend I use the method recommended in docs (code omitted for brevity).
enterprise = Enterprise() AttachmentFormSet = generic_inlineformset_factory(Attachment, form=AttachmentForm, max_num=3, validate_max=True) if request.method == 'POST': attachment_formset = AttachmentFormSet(request.POST, request.FILES, instance=enterprise, form_kwargs = {'user': request.user}) else: attachment_formset = AttachmentFormSet(instance=enterprise, form_kwargs = {'user': request.user})
But docs do not cover how achieve the same thing in backend using Django Admin.
In admin I have:
class AttachmentInline(GenericTabularInline): model = Attachment form = AttachmentForm class EnterpriseAdmin(MyCompareVersionAdmin): inlines = [AttachmentInline]
It's unclear where and how we need to grab and pass request.user parameter to AttachmentForm. After some trial and error, I ended up with this workaround:
class EnterpriseAdmin(admin.ModelAdmin): inlines = [AttachmentInline] def save_related(self, request, form, formsets, change): AttachmentForm.user = request.user super(EnterpriseAdmin, self).save_related(request, form, formsets, change)
Related modifications in AttachmentForm:
user = None def __init__(self, *args, **kwargs): user = kwargs.pop('user', None) if user: self.user = user super(AttachmentForm, self).__init__(*args, **kwargs)
So I turned user to class attribute and set it from kwargs only if it's passed and it's not None.
It works, but I don't like this approach and It looks like a hack for me.
I think the solution on how to properly do it should be added to the docs in the same section along with frontend solution.
I'm not sure If I can't find the proper way to do that or it's really not implemented for admin.
Change History (14)
comment:1 by , 9 years ago
| Description: | modified (diff) |
|---|
comment:2 by , 9 years ago
| Description: | modified (diff) |
|---|
comment:3 by , 9 years ago
| Description: | modified (diff) |
|---|
comment:4 by , 9 years ago
| Description: | modified (diff) |
|---|
comment:5 by , 9 years ago
| Description: | modified (diff) |
|---|
comment:6 by , 9 years ago
| Version: | 1.10 → master |
|---|
comment:7 by , 9 years ago
| Component: | contrib.admin → Documentation |
|---|---|
| Description: | modified (diff) |
| Summary: | Docs - Passing custom parameters to formset forms in admin → Passing custom parameters to formset forms in admin |
comment:8 by , 9 years ago
| Description: | modified (diff) |
|---|
comment:9 by , 9 years ago
comment:10 by , 9 years ago
@knbk I understand that approach with class attribute is not elegant. As for your soultion - I need to do it for formset form (AttachmentForm), not admin form. Admin form is different in my case and called EnterpriseAdminForm. Formset has no get_form related method, only get_form_kwargs(self, index): method. which has no reference to request.
comment:11 by , 9 years ago
InlineModelAdmin has a get_formset() method in which you can do the same.
comment:12 by , 9 years ago
@knbk Thanks, I think this can be also done in def get_formsets_with_inlines(self, request, obj=None): or def get_inline_formsets(self, request, formsets, inline_instances, obj=None): in regular admin.ModelAdmin.
comment:13 by , 9 years ago
Did you come up with a solution? If so, maybe you can submit a patch if you still think an example is needed?
comment:14 by , 9 years ago
| Component: | Documentation → contrib.admin |
|---|---|
| Resolution: | → duplicate |
| Status: | new → closed |
| Summary: | Passing custom parameters to formset forms in admin → Allow passing custom parameters to formset forms in admin |
| Type: | Uncategorized → New feature |
Closing as a duplicate of #26607 (see :ticket:26607#comment:3).
I'm currently using the following pattern to solve the same problem with forms:
def get_form(self, request, obj=None, **kwargs): Form = super().get_form(request, obj=None, **kwargs) return functools.partial(Form, user=request.user)Using a class attribute can suffer from thread-safety issues when handling concurrent requests.