arulmr
arulmr

Reputation: 8836

django admin overriding change_view with foreignkey filter

Here in my django ModelAdmin I want to filter foreignkey choices based on current user's group and I want to achieve it both in add and change form. I have achieved it in add view by using following code.

add_view

def add_view(self, request, form_url = '', extra_context = None):
    service_sector = common.getServiceSector(request.user)
    ModelForm = self.get_form(request)
    if request.POST:
        form = ModelForm(request.POST, request.FILES)
    else:
        form = ModelForm()
    if service_sector:
        qs = form['member'].field.queryset
        qs = qs.filter(service_sector = service_sector)
        form['member'].field.queryset = qs
    adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)),
    self.prepopulated_fields, self.get_readonly_fields(request),
    model_admin=self)
    context = {
    'adminform': adminForm,
    'is_popup': "_popup" in request.REQUEST,
    'show_delete': False,
    'root_path': self.admin_site.root_path,
    }
    context.update(extra_context or {})
    return self.render_change_form(request, context, form_url=form_url, add=True)

Here it is working fine. In this model I have a unique field Email and that is causing the problem in change_view. I have given my change_view code below.

change_view

def change_view(self, request, object_id, form_url = '', extra_context = None):
    qs = self.model._default_manager.get_query_set()

    service_sector = common.getServiceSector(request.user)
    bene_object = Beneficiary.objects.get(pk=object_id)
    ModelForm = self.get_form(request, bene_object)
    if request.POST:
        form = ModelForm(request.POST, request.FILES)
    else:                     

        form = ModelForm(instance=bene_object)            
    if service_sector:
        qs = form['member'].field.queryset
        qs = qs.filter(service_sector = service_sector)
        form['member'].field.queryset = qs
    adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)),
    self.prepopulated_fields, self.get_readonly_fields(request),
    model_admin=self)
    context = {
    'adminform': adminForm,
    'is_popup': "_popup" in request.REQUEST,
    'show_delete': False,
    'root_path': self.admin_site.root_path,
    }
    context.update(extra_context or {})
    return self.render_change_form(request, context, form_url = form_url, change = True)

Even though I have given change = True in my change_view return, it is trying to save the object as new one. So I'm getting an error Email already exists as the email field is unique value field. Or is it possible to use the normal change_view return as follows:

return super(ModelAdmin, self).change_view(request, form_url, extra_context)

If so, how can I filter foreignkey choices. Or how to use render_change_form to achieve this? Thanks in advance.

Upvotes: 1

Views: 6702

Answers (1)

arulmr
arulmr

Reputation: 8836

I have resolved this issue and posting the answer here which may help others. The question here helped me to fix this issue. With the following code I have done foreignkey filter based on user's group, for both add and change form in django admin with inline formsets in it. I'm giving the code below:

def render_change_form(self, request, context, *args, **kwargs):
    model = self.model
    opts = model._meta
    formsets = []

    #Change view
    if 'change' in kwargs.keys():
        object_id   = kwargs['obj'].id  #To get object id
        object      = Modelobjects.get(pk=object_id)
        ModelForm   = self.get_form(request, object)
        qs          = self.model._default_manager.get_query_set()
        if request.POST:
            form = ModelForm(request.POST, request.FILES)
        else:
            form = ModelForm(instance = object)
        service_sector = common.getServiceSector(request.user)
        if service_sector:
            #To filter foreignkey
            qs = form['member'].field.queryset
            qs = qs.filter(service_sector = service_sector)
            form['member'].field.queryset = qs
        adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)),
        self.prepopulated_fields, self.get_readonly_fields(request),
        model_admin=self)
        media = self.media + adminForm.media

        #To get inline formsets used
        prefixes = {}
        for FormSet, inline in zip(self.get_formsets(request),
                                   self.inline_instances):
            prefix = FormSet.get_default_prefix()
            prefixes[prefix] = prefixes.get(prefix, 0) + 1
            if prefixes[prefix] != 1:
                prefix = "%s-%s" % (prefix, prefixes[prefix])
            formset = FormSet(instance=object, prefix=prefix,
                              queryset=inline.queryset(request))
            formsets.append(formset)

        inline_admin_formsets = []
        for inline, formset in zip(self.inline_instances, formsets):
            fieldsets = list(inline.get_fieldsets(request))
            readonly = list(inline.get_readonly_fields(request))
            inline_admin_formset = helpers.InlineAdminFormSet(inline, formset,
                fieldsets, readonly, model_admin=self)
            inline_admin_formsets.append(inline_admin_formset)
            media = media + inline_admin_formset.media

    else:
        #Add view
        ModelForm   = self.get_form(request)
        qs          = self.model._default_manager.get_query_set()
        if request.POST:
            form = ModelForm(request.POST, request.FILES)
        else:
            form = ModelForm()
        service_sector = common.getServiceSector(request.user)
        if service_sector:
            qs = form['member'].field.queryset
            qs = qs.filter(service_sector = service_sector)
            form['member'].field.queryset = qs
        adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)),
        self.prepopulated_fields, self.get_readonly_fields(request),
        model_admin=self)
        media = self.media + adminForm.media

        prefixes = {}
        for FormSet, inline in zip(self.get_formsets(request),
                                   self.inline_instances):
            prefix = FormSet.get_default_prefix()
            prefixes[prefix] = prefixes.get(prefix, 0) + 1
            if prefixes[prefix] != 1:
                prefix = "%s-%s" % (prefix, prefixes[prefix])
            formset = FormSet(instance=self.model(), prefix=prefix,
                              queryset=inline.queryset(request))
            formsets.append(formset)

        inline_admin_formsets = []
        for inline, formset in zip(self.inline_instances, formsets):
            fieldsets = list(inline.get_fieldsets(request))
            readonly = list(inline.get_readonly_fields(request))
            inline_admin_formset = helpers.InlineAdminFormSet(inline, formset,
                fieldsets, readonly, model_admin=self)
            inline_admin_formsets.append(inline_admin_formset)
            media = media + inline_admin_formset.media

    context = {
    'adminform': adminForm,
    'title': _('Add %s') % force_unicode(opts.verbose_name),
    'is_popup': "_popup" in request.REQUEST,
    'show_delete': False,
    'media': mark_safe(media),
    'inline_admin_formsets': inline_admin_formsets,
    'root_path': self.admin_site.root_path,
    }
    return super(ModelAdmin, self).render_change_form(request, context, args, kwargs)

Here the object id is obtained from kwargs['obj'].id and foreignkey is filtered by

qs = form['member'].field.queryset
qs = qs.filter(service_sector = service_sector)
form['member'].field.queryset = qs

Here common.getServiceSector is a custom method I'm using for my foreignkey filter conditions. You can define your own method for your requirements. Hope this will be helpful to others. Thanks.

Upvotes: 5

Related Questions