在django admin中基于action表单创建一个动态列



我试图在django admin上创建一个列,其值根据从一个动作表单

的输入而变化。

例如

#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)

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

正如我在其他答案中所说,没有简单的方法可以在列方法中获得request。但是,即使它是可能的,它也将不同于你的action方法。

如果希望每次都计算距离,可以将经纬度保存在更永久的存储中。它可以是数据库、缓存或用户会话。保存到会话的例子:

@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

不幸的是,在distance方法中没有简单的解决方案来获得您的请求。另外,这个请求会和post请求不同,因为django admin会在action完成处理后自动重定向,但是我们可以通过在action中返回response来防止这种情况。

当我们在动作中返回响应时,我们可以将经纬度保存到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

最新更新