[Fixed]-Creating a dynamic column in django admin based on an action form

1๐Ÿ‘

As I said in other answer, there is no simple way to get request in column method. But even if it will be possible, it will be different from one in your action method.

If we want to have our distance calculated every time, we can save our latitude and longitude in some more permanent storage. It can be database, cache or user session. Example with saving into session:

@register(Distance)
class DistanceAdmin(admin.ModelAdmin):
    list_display = ['latitude','longitude'] # not displaying distance by default
    list_display_alt = ['latitude', 'longitude', 'distance'] # we will use that one if this was proper action call
    action_form = CalculateDistanceForm
    actions = ['update_distance']

    def changelist_view(self, request, *args, **kwargs):
        # We can't get user session in our column method, so we will copy our values from that session into `ModelAdmin` instance here:        

        if request.session.get('admin_latitude') and request.session.get('admin_longitude'):
            self.latitude = request.session['admin_latitude']
            self.longitude = request.session['admin_longitude']
        return super(DistanceAdmin, self).changelist_view(self, request, *args, **kwargs)


    def distance(self,obj):
        return distance_calculator(obj.latitude,obj.longitude, self.latitude, self.longitude)

    def get_list_display(self, request):
        if hasattr(self, 'latitude') and hasattr(self, 'longitude'): # that means it was our action call, so we will modify default columns
            return list_display_alt
        return super(DistanceAdmin, self).get_list_display(request)

    def update_distance(self,request,queryset):
        #self.latitude = request.POST.get('latitude',None)
        #self.longitude = request.POST.get('longitude',None)
        # it will better to use form here instead of raw POST processing

        form = CalculateDistanceForm(request.POST, request.FILES)
        form.fields['action'].choices = (('update_distance', "Update distance"), ) # this is necessary because default ActionForm has no idea about valid actions

        if form.is_valid():
            latitude = form.cleaned_data['latitude']
            longitude = form.cleaned_data['longitude']

            request.session['admin_latitude'] = latitude
            request.session['admin_longitude'] = longitude
        else:
            # if form wasn't valid, we can inform about that using messages framework here
๐Ÿ‘คGwynBleidD

0๐Ÿ‘

Unfortunately, there is no easy solution to get your request in distance method. Also, that request will be different that your post request because django admin will do automatic redirection after action is done processing, but we can prevent that by returning response inside your action.

When we are returning response in action, we can save latitude and longitude into ModelAdmin instance and retrieve it later in distance method.

@register(Distance)
class DistanceAdmin(admin.ModelAdmin):
    list_display = ['latitude','longitude'] # not displaying distance by default
    list_display_alt = ['latitude', 'longitude', 'distance'] # we will use that one if this was proper action call
    action_form = CalculateDistanceForm
    actions = ['update_distance']

    def distance(self,obj):
        return distance_calculator(obj.latitude,obj.longitude, self.latitude, self.longitude)

    def get_list_display(self, request):
        if hasattr(self, 'latitude') and hasattr(self, 'longitude'): # that means it was our action call, so we will modify default columns
            return list_display_alt
        return super(DistanceAdmin, self).get_list_display(request)

    def update_distance(self,request,queryset):
        #self.latitude = request.POST.get('latitude',None)
        #self.longitude = request.POST.get('longitude',None)
        # it will better to use form here instead of raw POST processing

        form = CalculateDistanceForm(request.POST, request.FILES)
        form.fields['action'].choices = (('update_distance', "Update distance"), ) # this is necessary because default ActionForm has no idea about valid actions

        if form.is_valid():
            self.latitude = form.cleaned_data['latitude']
            self.longitude = form.cleaned_data['longitude']

            request.method = GET  # tricking default changelist_view to think that there is no action called, without that we will end up in infinite loop.

            return self.changelist_view(request)
        else:
            # if form wasn't valid, we can inform about that using messages framework here
๐Ÿ‘คGwynBleidD

Leave a comment