基于动作表单在django admin中创建动态列

时间:2022-09-11 18:22:31

I am trying to create a column on django admin whose values changes based on the input from an action form

我正在尝试在django admin上创建一个列,其值根据操作表单的输入而更改

So for example

所以举个例子

#Action form
class CalculateDistanceForm(ActionForm):        
    latitude = forms.DecimalField()
    longitude = forms.DecimalField(initial=0)

#Admin
@register(Distance)
class DistanceAdmin(admin.ModelAdmin):
    list_display = ['latitude','longitude','distance']
    action_form = CalculateDistanceForm
    actions = ['update distance']

    def distance(self,obj):
        # how do i get the request instance to determine the post parameters on the form
        return distance_calculator(obj.latitude,obj.longitude)

    def update_distance(self,request,queryset):
        lat = request.POST.get('latitude',None)
        lon = request.POST.get('longitude',None)
        queryset.calculate_price(lat,lon)

My question is how can i dynamically display the new distance generated by the queryset?

我的问题是如何动态显示查询集生成的新距离?

2 个解决方案

#1


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

#2


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.

不幸的是,没有简单的解决方案来获取您的远程方法请求。此外,该请求将与您的发布请求不同,因为django admin将在操作完成处理后执行自动重定向,但我们可以通过在您的操作中返回响应来阻止该请求。

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

当我们返回响应时,我们可以将经度和经度保存到ModelAdmin实例中,稍后在distance方法中检索它。

@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

#1


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

#2


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.

不幸的是,没有简单的解决方案来获取您的远程方法请求。此外,该请求将与您的发布请求不同,因为django admin将在操作完成处理后执行自动重定向,但我们可以通过在您的操作中返回响应来阻止该请求。

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

当我们返回响应时,我们可以将经度和经度保存到ModelAdmin实例中,稍后在distance方法中检索它。

@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