2009-06-11 74 views
13

我有一个模型,其中一个字段是一个ForeignKey值,它的模型为40,000行。默认的模型形式尝试创建一个包含40,000个选项的选择框,至少可以说是不理想的。当这个模型在一个formset工厂中使用时更是如此!raw_id_fields for modelforms

在管理员中,通过使用“raw_id_fields”很容易避免这种情况,但似乎没有模型等效。我怎样才能做到这一点?

这里是我的ModelForm:

class OpBaseForm(ModelForm): 

    base = forms.CharField() 

    class Meta: 
     model = OpBase 
     exclude = ['operation', 'routes'] 
     extra = 0 
     raw_id_fields = ('base',) #does nothing 

第一加粗线的工作原理是不是创造了巨大笨重的选择框,但是当我试图挽救这种形式的一个字段,我得到的错误:“OpBase。基地“必须是”基地“的实例。为了保存模型,'base'需要是Base实例。显然,基本主键的字符串表示是不够的(至少不会自动)。我需要某种机制将给定我的表单的字符串更改为Base实例。而这个机制必须在formset中工作。有任何想法吗?如果只有raw_id_fields可以起作用,那么这就像蛋糕一样容易。但据我所知,它只能在管理员中使用。

回答

10

您需要更改小部件base字段,而不是字段类型。我认为这会工作:

class OpBaseForm(ModelForm): 
    base = forms.ModelChoiceField(queryset=Base.objects.all(), 
            widget=forms.TextInput) 

    class Meta: 
     model = OpBase 
     ... etc... 
+1

只是为了澄清,raw_id_field属性是ModelAdmin属性http://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.raw_id_fields不是ModelForm属性。 – 2011-02-09 08:22:30

13

您也可以使用整个raw_id_field管理部件,完全与后台管理页面有方便的JS弹出搜索。你甚至不需要模型表单。具体方法如下:

import string 
from django.contrib.admin.widgets import ForeignKeyRawIdWidget 
from django import forms 
from models import MyModel 

# Have to subclass widget b/c 
# django hardcodes a relative path to Admin Root URL: ../../.. 
class HardcodedURLForeignKeyRawIdWidget(ForeignKeyRawIdWidget): 
    def render(self, *args, **kwargs): 
     original_render = super(HardcodedURLForeignKeyRawIdWidget, 
      self).render(*args, **kwargs) 
     ADMIN_ROOT_URL = "/admin/" 
     return string.replace(original_render,"../../../", ADMIN_ROOT_URL) 


class FieldLookupForm(forms.Form): 
    my_foreignkey_field = forms.CharField(max_length=10, 
     widget=HardcodedURLForeignKeyRawIdWidget(
      MyModel._meta.get_field("foreignkey_field").rel)) 

添加相关管理员JS给您的模板,中提琴

{% block header %} 
<script type="text/javascript">window.__admin_media_prefix__ = "/static/admin/";</script> 
<script type="text/javascript" src="/admin/jsi18n/"></script> 
<script type="text/javascript" src="/static/admin/js/core.js"></script> 
<script type="text/javascript" src="/static/admin/js/admin/RelatedObjectLookups.js"></script> 
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script> 
<script type="text/javascript" src="/static/admin/js/jquery.init.js"></script> 
<script type="text/javascript" src="/static/admin/js/actions.min.js"></script> 
{% endblock %} 
+1

在Django 1.4中,它看起来像ForeignKeyRawIdWidget的构造函数发生了改变 - 使用这段代码导致它抛出__init __()只需要3个参数(给出2个参数)。如果您已导入django.contrib.admin,则可以将admin.site作为缺少的参数直接传递给ForeignKeyRawIdWidget,而不必将其与HardcodedUrl版本一起包装。 – Voltaire 2012-07-31 04:55:30

+0

你必须使用`admin.autodiscover()`来完成这项工作。我正在写这个答案,因为缺少了一点。 – chriscauley 2012-12-17 16:03:51

12

为上述扩大伏尔泰的评论,Django的1.4的解决方案是:

from django.contrib import admin 
admin.autodiscover() 

from django.contrib.admin.widgets import ForeignKeyRawIdWidget 
from django import forms 

from .models import Post, Photo 

class PostForm(forms.ModelForm): 
    photo = forms.ModelChoiceField(
     Photo.objects.all(), 
     widget=ForeignKeyRawIdWidget(Post._meta.get_field("photo").rel,admin.site) 
    ) 

而只有额外的JavaScript你应该需要的是:

<script type="text/javascript" src="/static/admin/js/admin/RelatedObjectLookups.js"></script> 

这里重要的是你在管理员上调用自动发现,否则你的RawIdWidget将没有链接。此外,ModelChoiceField需要一个实际未使用的查询集。 ModelChoiceField比CharField更可取,因为CharField不能正确验证(尝试保存id而不是查找Photo实例)。