Action -> massage user data --> update db with massaged data

Hello,
In my application, I have added an action to a modeladmin class. I want to allow users to select objects in an admin view, put up a form (using default modelform) that will allow me to get data from the user and massage it. Then create bulk entries in the db with mentioned, data.

Currently,

  • I have the code to massage the data in a function inside the modeladmin class and it redirects to a view (hoping to send the data to the view).
  • I have a form that uses modelform, excluding some fields that don’t apply due to the multiselect nature of the action.
  • I have a view that should be displaying the form, receiving the data, and performing the creation of the bulk entries.
  • I have a very basic template
  • My urls are there but I’m unsure how to make it work.

My Issue:
I am trying to redirect from the modeladmin class (function inside) to the view but the data is a dictionary of bulk data. Clearly I have designed it incorrectly but I’m unsure how to make it right. Should I just be doing all of the work (including submitting the new db entries) right in the modeladmin class? FYI: I originally had the function for the action in the admin.py outside of the modeladmin class but I’m currently grasping at straws so things are getting rearranged.

models.py


class AutoBlock(models.Model):
    unimportant info

class Pool(models.Model):
    unimportant info

admin.py

class PoolAdmin(ImportExportMixin, admin.ModelAdmin):
    list_per_page = 10
    list_display = ("id","name", "scheduler", "clusters", 'can_run_check_cluster', 'job_queue_enable')
    ordering = ("name",)
    list_display_links = ('id',)
    list_editable = ("name", "scheduler", 'can_run_check_cluster', 'job_queue_enable')
    search_fields = ("name", "scheduler__description")
    actions = ['**activate_autoblock_selected_action**']
               
    def **activate_autoblock_selected_action**(self, request, queryset):
        clusters_list = Cluster.objects.all()
        cluster_list = []

        # Get the object ids for the selected objects (pools or schedulers)
        object_id_list = []
        queryset_type = queryset[0].__class__
        for obj in queryset:
            object_id_list.append(obj.id)
    
        # Get the list of clusters that have the selected objects associated
        for x in clusters_list:
            if x not in cluster_list:
                obj_id = None
                if queryset_type is Scheduler and x.scheduler:
                    obj_id = x.scheduler.id
                elif x.pool:
                    obj_id = x.pool.id
                if obj_id in object_id_list:
                    cluster_list.append(x)

        all_with_details = {}
        # Get all the clusters with details into a dictionary
        for cluster in cluster_list:
            scheduler = cluster.scheduler
            all_with_details[cluster.get_cluster_ip()] = {"scheduler_ip": scheduler.ip,
                    "scheduler_name": scheduler.description}
                    
        # Send dictionary of new cluster data and list of existing inactive autoblocks to update
        return redirect('add_auto_block_bulk', kwargs=all_with_details)

    activate_autoblock_selected_action.short_description = "Activate autoblock on selected objects"

views.py

@require_http_methods(["POST"])
@csrf_exempt
def add_auto_block_bulk(request, **kwargs):

    scaleio_build = ""
    spark_test = ""
    report_link = ""
    error = ""

    # launch form to get user input
    form = AddUpdateAutoblockForm(request.POST)
    
    # Verify form and collect user input
    if form.is_valid():
        scaleio_build = form.cleaned_data['scaleio_build']
        spark_test = form.cleaned_data['spark_test']
        report_link = form.cleaned_data['report_link']
        error = form.cleaned_data['error']
    
        try:
            req_str = request.body.decode('utf-8').replace('\\', '')
            body = json.loads(req_str)

            # Populate list of new AutoBlock objects including user data and selection data
            autoblocks = [AutoBlock(cluster_ip=cluster, 
                                    scheduler_name=details["scheduler_name"], 
                                    scheduler_ip=details["scheduler_ip"], 
                                    scaleio_build=scaleio_build,
                                    spark_test=spark_test, 
                                    report_link=report_link, 
                                    error=error) for cluster, details in kwargs["items_to_add"].items()]

            # create new AutoBlock entries in the database
            new_auto_block = AutoBlock.objects.bulk_create(autoblocks)
            new_auto_block.save()
            add_sucess = True

            # update existing inactive AutoBlock table entries to be active
            AutoBlock.objects.bulk_update(kwargs["objects_to_update"], ['active'])

            return HttpResponse(status=200)
        except Exception as e:
            if add_sucess:
                log_warning_response("Failed to add auto blocks, exp {}".format(str(e)), 400)
            else: 
                log_warning_response("Failed to update auto block, exp {}".format(str(e)), 400)
            return HttpResponse(str(e), status=400)
    else:
        form = AddUpdateAutoblockForm()

    return render(request, 'form_post_template.html', {'form': form})

forms.py - this excludes any fields that can’t apply to a bulk operation and sets dummy data for fields that require input. The dummy data will be replaced for each entry that is to be added to the db during the massaging of the data.

class AddUpdateAutoblockForm(ModelForm):
    class Meta:
        model = AutoBlock
        exclude = ['scheduler_ip', 'scheduler_name', 'cluster_ip', 'active']
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['cluster_ip'] = "1.1.1.1"
        self.fields['active'] = True

form_post_template.html

 <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Submit</button>
    </form>

urls.py

urlpatterns = [
    
    path(r'add_auto_block_bulk/', views.add_auto_block_bulk),

Hi,

The kwargs argument in redirect() is used for URL reversing/resolving, not for passing request data.

You may want to look at Actions that provide intermediate pages.

Most of the time, the best practice will be to return an HttpResponseRedirect and redirect the user to a view you’ve written, passing the list of selected objects in the GET query string.

You might want something similar to export_selected_objects() action example from the docs. In your redirected view, you could parse object ids, manipulate data and populate the form.