【问题标题】:Django admin form - how to change dynamically select valuesDjango 管理表单 - 如何动态更改选择值
【发布时间】:2012-04-12 23:08:41
【问题描述】:

我有这种情况: 我有模型项目、地区和国家。

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)

当我在管理区域中添加项目并选择国家/地区时,我想自动构建区域,仅从选定的 Country 中选择 Regions

我知道如何在 javascript 中做到这一点,但我不知道在 Django 中做到这一点有多正确。

【问题讨论】:

  • 您需要使用 ajax 来执行此操作:当第一个选择的值(国家/地区)发生变化时,对该国家/地区的所有区域执行 ajax 请求,并使用这些值过滤第二个选择。
  • 我知道,但我还有另一个问题 - 如何在 Django 上正确操作。我可以在管理区域的模板中插入 javascript 代码并生成另一个选择。 $('#select1').change(function(){ $,ajax({ }) })
  • 还有django-smart-selects看起来不错
  • 我遵循了这个问题中描述的内容,但我不确定我是否完成了所有必要的步骤:为了从一个地区快速选择一个国家,我是否还需要添加一些 javascript 代码?

标签: django django-admin


【解决方案1】:

您需要覆盖管理表单的 clean 方法:

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 

【讨论】:

  • 谢谢,你是个好人。我尝试了 make def clear_region,但我以错误的方式制作。并在您的 def clean 需要添加行 return self.cleaned_data
  • 很好,我编辑了我的答案(这是我项目中的错误复制粘贴)
  • @FrancisYaconiello 是与 Item 模型相关的 clean 方法吗?您的代码中的 CustomItemForm 是什么?
【解决方案2】:

如果你在让 django 接受你选择的值时遇到问题,因为它认为它不应该首先存在,让查询集引用每个可能的值,然后覆盖“选择”属性与一个空集。后一部分将避免 django 构建一个巨大的选项列表,该列表将被动态 javascript 覆盖。

我知道这不适用于管理员屏幕,但我在为非管理员做类似的事情时发现了这个页面,所以我想我也应该在这里回答。这是我正在使用的:

在views.py中:

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>

【讨论】:

    【解决方案3】:

    我没有通过提议的库(使用简单的 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)
    

    【讨论】:

    • 我昨天刚刚在我的一个应用程序中解决了这个问题,请参阅下面的答案
    • 另外,在您的 __init__ 方法中,如果 kwargs['instance'] 未设置并抛出异常 country = 1 将在 Region.objects.fitler(country=country) 代码行中抛出错误 b/c 过滤器需要一个对象实例不是一个整数。仅供参考,可能会失败
    猜你喜欢
    • 2011-08-31
    • 2015-09-09
    • 1970-01-01
    • 2020-12-11
    • 1970-01-01
    • 2019-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多