2009-06-11 45 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實例)。