Подтвердить что ты не робот

Raw_id_fields для моделей

У меня есть модель, у которой есть одно поле, которое является значением ForeignKey для модели, которая составляет 40 000 строк. Модель модели по умолчанию пытается создать поле выбора с 40 000 опциями, что, мягко говоря, не идеально. Тем более, когда этот modelform используется в formet factory!

В admin это легко избежать, используя "raw_id_fields", но похоже, что эквивалент модели не эквивалентен. Как я могу это сделать?

Вот моя модель:

class OpBaseForm(ModelForm):

    base = forms.CharField()

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

Первая полужирная линия работает, не создавая огромный неуправляемый selectbox, но когда я пытаюсь сохранить набор полей этой формы, я получаю ошибку: "OpBase.base" должен быть экземпляром "Base". Чтобы сохранить модельную форму, "базой" должен быть экземпляр Base. По-видимому, строковое представление базового первичного ключа недостаточно (по крайней мере, не автоматически). Мне нужен какой-то механизм для изменения строки, заданной моей формой, для экземпляра Base. И этот механизм должен работать в форме. Есть идеи? Если бы работал только raw_id_fields, это было бы легко, как торт. Но, насколько я могу судить, он доступен только в админе.

4b9b3361

Ответ 1

Вам нужно изменить виджет для поля base, а не тип поля. Я думаю, что это сработает:

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

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

Ответ 2

Вы также можете использовать весь виджет администратора 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 %}

Ответ 3

Чтобы расширить комментарий Вольтера выше, решение 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 не проверяет должным образом (пытается сохранить идентификатор, а не искать экземпляр Photo).

Обновление

Django 2.0 устарел Field.rel в пользу Field.remote_field.

Эта строка widget= теперь должна быть:

widget=ForeignKeyRawIdWidget(Post._meta.get_field("photo").remote_field, admin.site),