Django管理表单 - 如何动态改变选择值

问题描述:

我有这种情况: 我有模型项目,地区和国家。Django管理表单 - 如何动态改变选择值

class Item(models.Model): 
    name = models.CharField(max_length=255) 
    alias = models.SlugField(unique=True) 
    country = models.OneToOneField(Country, default=0) 
    region = models.OneToOneField(Region, default=0, related_name='') 

class Region(models.Model): 
    name = models.CharField(max_length=100) 
    country = models.ForeignKey(Country, default=0) 

class Country(models.Model): 
    name = models.CharField(max_length=100) 

当我在管理方面增加一个项目,我选择的国家我想自动建立本地区与Regions只从选定Country选择。

我知道如何在JavaScript中做到这一点,但我不知道它是如何正确的在Django中做到这一点。

+0

您将需要使用Ajax做

在views.py

这个:当第一次选择变化(国家)的值时,为该国家的所有地区做一个ajax请求并用这些值过滤第二次选择。 – 2012-03-29 10:38:54

+0

我知道,但我还有一个问题 - 它如何在Django上做对。 我可以在管理区的模板中插入javascript代码并生成另一个选择。 $( '#选择1')。改变(函数(){$ ,AJAX({ }) }) – yAnTar 2012-03-29 12:05:17

+1

有一些Django的包,比如http://code.google.com/p/django-ajax -filtered-fields /和https:// github。com/twidi/django-ajax-select你可以试试这些 – Pannu 2012-03-29 14:59:53

你需要重写你的管理形式的清洁方法:

def clean(self): 
    super(CustomItemForm, self).clean() #if necessary 
    if 'region' in self._errors: 
     """  
     reset the value (something like this i 
     think to set the value b/c it doesnt get set 
     b/c the field fails validation initially) 
     """ 
     region = Region.objects.get(pk=self.data['region']) 
     self.initial['region'] = region.id 
     self.cleaned_data['region'] = region 
     self.region = region 

     # remove the error 
     del self._errors['region'] 

    return self.cleaned_data 
+0

谢谢,你是伟大的人。 我试图让def clear_region,但我做错了方式。 而在你的清洁需要添加行 返回self.cleaned_data – yAnTar 2012-04-05 14:01:22

+0

好赶上,我编辑我的答案(这是从我的项目坏的复制粘贴) – 2012-04-05 14:06:30

Soory为我的延迟(我有其他任务)。

我解决了这个问题不建议库(与简单的JavaScript),但存在一个问题:当我改变国家 - 选择区域自动改变,但我不能保存新的区域值(当来自同一国家的另一个区域 - 没有问题),因为错误选择一个有效的选择。该选择不是可用选择之一。

模型是在第一个问题是相同的,但对模范国家等于默认值1

我的第一个办法是改变国家通过formfield_key功能选择,

def formfield_for_foreignkey(self, db_field, request, **kwargs): 
    if db_field.name == 'region': 
     kwargs["queryset"] = Region.objects.filter(country=self.country) 
     return db_field.formfield(**kwargs) 

,但我不知道是保存对象,还是编辑对象。

Here写道 - 最好的办法是通过形式 变化,现在我有代码:

class Item(models.Model): 
    country = models.ForeignKey(Country, default=1) 
    region = models.ForeignKey(Region, related_name='') 


class ItemAdmin(admin.ModelAdmin): 
    form = CustomItemForm 
    prepopulated_fields = {"alias": ("name",)} 
    list_filter = ('country', 'category',) 

class CustomItemForm(forms.ModelForm): 
    def __init__(self, *args, **kwargs): 
     try: 
      country = kwargs['instance'].country 
     except KeyError: 
      country = 1 
     super(CustomItemForm, self).__init__(*args, **kwargs) 
     self.fields['region'].queryset = Region.objects.filter(country=country) 
+0

我刚刚在我的一个应用程序昨天解决了这个问题,在我的'__init__'方法中,如果'kwargs ['instance']'没有设置并且抛出异常'country = 1',我的回答也会在 – 2012-04-05 13:41:53

+0

的下方抛出'Region.objects.fitler(country = country)'代码行中的错误b/c过滤器期望对象实例不是int。只是FYI可能会失败 – 2012-04-05 13:52:20

如果”在django接受你选择的值时遇到麻烦,因为它不认为它应该在那里出现,让查询集引用每个可能的值,但是然后用空集覆盖“选择”属性。后一部分将避免django构造一个巨大的选项列表,这个选项列表正好被动态javascript覆盖。

我知道这不是管理员屏幕可以使用的功能,但是我在为非管理员做类似的事情时发现了这个页面,所以我想我也会在这里回答它。下面是我使用的是什么:

from django.shortcuts import render_to_response 
from django import forms 
from django.template import RequestContext 
from django.http import HttpResponseRedirect, HttpResponse 
from django.core.urlresolvers import reverse 
from django.core import serializers 
# other imports 

class AddRouteForm (forms.Form): 
    router = forms.ModelChoiceField (queryset=Router.objects.all()) 
    peer = forms.ModelChoiceField (queryset=Peer.objects.all()) 
    peer.choices = [] 
    # other stuff not relevant 

def get_peers (request): 
    router_id = request.GET['router_id'] 
    router = Router.objects.get(id=router_id) 
    data = serializers.serialize ("json", router.peer_set.filter(transit_capable=True)) 
    return HttpResponse (data, content_type="text/json") 

def add_route (request): 
    if request.method == "POST": 
     form = AddRouteForm (request.POST) 
     if form.is_valid(): 
      # TODO something here 
      return HttpResponseRedirect (reverse ("staticroute.views.index")) 
    else: 
     form = AddRouteForm() 
    return render_to_response ("staticroute/add_route.html", locals(), RequestContext(request)) 

在HTML文件(jQuery的需要太装):

<form method="POST">{% csrf_token %} 
{{ form.as_p }} 
<input type="submit" value="Add Route" /> 
</form> 
<script> 
    $("#id_router").change (function() { 
     var router_id = $('#id_router').val(); 

     var items = []; 
     items.push ('<option value="" selected="selected">---------</option>'); 
     $("#id_peer").html (items.join('')); 

     if (router_id != "") { 
      $.getJSON ('{% url staticroute.views.get_peers %}', {router_id:router_id}, function(data) { 
       $.each (data, function (index, val) { 
        items.push ('<option value="' + val.pk + '">' + val.fields.description + ' (' + val.fields.address + ')</option>'); 
       }); 
       $("#id_peer").html (items.join(''));   
      }); 
     } 

    }).change(); 
</script>