我试图在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