Django-поле формы nonrel для ListField

я экспериментирую с django-nonrel на appengine и пытается использовать djangotoolbox.fields.ListField для реализации отношения "многие ко многим". Как я читал в документации a ListField это то, что вы можете использовать, чтобы сделать обходной путь для djamgo-nonrel, не поддерживая отношения "многие ко многим".

это выдержка из моей модели:

class MyClass(models.Model):
    field = ListField(models.ForeignKey(AnotherClass))

поэтому, если я получаю это право, я создаю список внешних ключей к другому классу, чтобы показать связь с несколькими экземплярами другого класс!--13-->

при таком подходе все работает нормально ... ни одно исключение. Я могу создавать объекты MyClass в коде и представлениях. Но когда я пытаюсь использовать интерфейс администратора, я получаю следующую ошибку

No form field implemented for <class 'djangotoolbox.fields.ListField'>

поэтому я подумал, что попробую что-то, чего раньше не делал. Создайте свое собственное поле. Ну на самом деле моя собственная форма для редактирования MyClass экземпляры в интерфейсе администратора. Вот что я сделал:--13-->

class MyClassForm(ModelForm):
    field = fields.MultipleChoiceField(choices=AnotherClass.objects.all(), widget=FilteredSelectMultiple("verbose_name", is_stacked=False))
    class Meta:
        model = MyClass

тогда я пас MyClassForm как форму для админа интерфейс

class MyClassAdmin(admin.ModelAdmin):
    form = MyClassForm

admin.site.register(MyClass, MyClassAdmin)

я думал, что это сработает, но это не так. Когда я иду в интерфейс администратора, я получаю ту же ошибку, что и раньше. Может кто-нибудь сказать, что я делаю неправильно здесь ... или если у вас есть какие-либо другие предложения или истории успеха с помощью ListField, SetField, etc. от djangotoolbox.fields в интерфейсе администратора это было бы очень признательно.

4 ответов


хорошо, вот что я сделал, чтобы все это работало ... Я начну с самого начала!--5-->

вот как выглядела моя модель

class MyClass(models.Model):
    field = ListField(models.ForeignKey(AnotherClass))

Я хотел бы иметь возможность использовать интерфейс администратора для создания / редактирования экземпляров этой модели с помощью виджета множественного выбора для поля списка. Поэтому я создал некоторые пользовательские классы следующим образом

class ModelListField(ListField):
    def formfield(self, **kwargs):
        return FormListField(**kwargs)

class ListFieldWidget(SelectMultiple):
    pass

class FormListField(MultipleChoiceField):
    """
    This is a custom form field that can display a ModelListField as a Multiple Select GUI element.
    """
    widget = ListFieldWidget

    def clean(self, value):
        #TODO: clean your data in whatever way is correct in your case and return cleaned data instead of just the value
        return value

эти классы позволяют использовать listfield в admin. Затем я создал форму для использования в admin сайт

class MyClassForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(MyClasstForm,self).__init__(*args, **kwargs)
        self.fields['field'].widget.choices = [(i.pk, i) for i in AnotherClass.objects.all()]
        if self.instance.pk:
            self.fields['field'].initial = self.instance.field

    class Meta:
        model = MyClass

после этого я создал модель администратора и зарегистрировал ее на сайте администратора

class MyClassAdmin(admin.ModelAdmin):
    form = MyClassForm

    def __init__(self, model, admin_site):
        super(MyClassAdmin,self).__init__(model, admin_site)

admin.site.register(MyClass, MyClassAdmin)

теперь это работает в моем коде. Имейте в виду, что этот подход может совсем не подходить для google_appengine, поскольку я не очень разбираюсь в том, как он работает, и он может создавать неэффективные запросы.


насколько я понимаю, вы пытаетесь иметь отношения M2M в django-nonrel, что не является нестандартной функциональностью. Для начала, если вы хотите быстро взломать, вы можете пойти с этим простым классом и использовать CharField для ввода внешних ключей вручную:

class ListFormField(forms.Field):
    """ A form field for being able to display a djangotoolbox.fields.ListField. """

    widget = ListWidget

    def clean(self, value):
        return [v.strip() for v in value.split(',') if len(v.strip()) > 0]

но если вы хотите иметь множественный выбор из списка моделей, как правило, вам придется использовать ModelMultipleChoiceField, который также не работает в django-nonrel. Вот что я сделал, чтобы подражать M2M отношение с использованием MultipleSelectField:

предположим, у вас есть отношение M2M между 2 классами, SomeClass и AnotherClass соответственно. Вы хотите выбрать отношение в форме для SomeClass. Также я предполагаю, что вы хотите сохранить ссылки как ListField в SomeClass. (Естественно, вы хотите создать отношения M2M, как они объясняются здесь, чтобы предотвратить взрывающиеся индексы, если вы работаете над App Engine).

Итак, у вас есть ваши модели например:

class SomeClass(models.Model):
    another_class_ids = ListField(models.PositiveIntegerField(), null=True, blank=True)
    #fields go here

class AnotherClass(models.Model):
    #fields go here

и в форме:

class SomeClassForm(forms.ModelForm):

    #Empty field, will be populated after form is initialized
    #Otherwise selection list is not refreshed after new entities are created.
    another_class = forms.MultipleChoiceField(required=False)

def __init__(self, *args, **kwargs):
    super(SomeClassForm,self).__init__(*args, **kwargs)
    self.fields['another_class'].choices = [(item.pk,item) for item in AnotherClass.objects.all()]

    if self.instance.pk: #If class is saved, highlight the instances that are related
        self.fields['another_class'].initial = self.instance.another_class_ids

def save(self, *args, **kwargs):  
    self.instance.another_class_ids = self.cleaned_data['another_class']         
    return super(SomeClassForm, self).save()

class Meta:
    model = SomeClass

надеюсь, это должно заставить вас начать, я реализовал эту функциональность для обычных форм, настроить ее для панели администратора не должно быть так сложно.


Это может быть не связано, но для интерфейса администратора убедитесь, что у вас есть djangotoolbox, указанный после django.ВНО.администратор в настройках.. INSTALLED_APPS


вы можете избежать пользовательского класса формы для такого использования, запросив объект модели

class ModelListField(ListField):
  def __init__(self, embedded_model=None, *args, **kwargs):
  super(ModelListField, self).__init__(*args, **kwargs)
  self._model = embedded_model.embedded_model

  def formfield(self, **kwargs):
    return FormListField(model=self._model, **kwargs)

class ListFieldWidget(SelectMultiple):
  pass

class FormListField(MultipleChoiceField):
  widget = ListFieldWidget

  def __init__(self, model=None, *args, **kwargs):
    self._model = model
    super(FormListField, self).__init__(*args, **kwargs)
    self.widget.choices = [(unicode(i.pk), i) for i in self._model.objects.all()]

  def to_python(self, value):
    return [self._model.objects.get(pk=key) for key in value]

  def clean(self, value):
    return value