9 Stimmen

Django-nonrel Formularfeld für ListField

Ich experimentiere mit django-nonrel auf appengine und dem Versuch, eine djangotoolbox.fields.ListField um eine Many-to-many-Beziehung zu implementieren. Wie ich in der Dokumentation gelesen habe, ist eine ListField ist etwas, das Sie verwenden können, um eine Umgehung für djamgo-nonrel zu schaffen, das keine Many-to-many-Beziehungen unterstützt.

Dies ist ein Auszug aus meinem Modell:

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

Also, wenn ich das richtig verstehe, erstelle ich eine Liste von Fremdschlüsseln zu einer anderen Klasse, um eine Beziehung mit mehreren Instanzen einer anderen Klasse anzuzeigen

Mit diesem Ansatz funktioniert alles gut ... Keine Ausnahmen. Ich kann "MyClass"-Objekte in Code und Ansichten erstellen. Aber wenn ich versuche, die Verwaltungsschnittstelle zu verwenden, erhalte ich den folgenden Fehler

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

Also dachte ich, ich probiere etwas aus, was ich noch nie gemacht habe. Mein eigenes Feld erstellen. Nun, eigentlich mein eigenes Formular für die Bearbeitung MyClass Instanzen in der Verwaltungsoberfläche. So habe ich es gemacht:

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

dann passe ich MyClassForm als das zu verwendende Formular für die Verwaltungsoberfläche

class MyClassAdmin(admin.ModelAdmin):
    form = MyClassForm

admin.site.register(MyClass, MyClassAdmin)

Ich dachte, das würde funktionieren, aber das tut es nicht. Wenn ich die Verwaltungsoberfläche aufrufe, erhalte ich die gleiche Fehlermeldung wie zuvor. Kann mir jemand sagen, was ich hier falsch mache ... oder ob Sie andere Vorschläge oder Erfolgsgeschichten zur Verwendung der ListField , SetField usw. aus djangotoolbox.fields in der Verwaltungsschnittstelle wäre es sehr zu begrüßen.

11voto

Rman Punkte 230

OK, hier ist, was ich getan habe, um das alles zum Laufen zu bringen ... Ich fange ganz von vorne an

So sah mein Modell aus

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

Ich wollte in der Lage sein, die Admin-Schnittstelle zum Erstellen/Bearbeiten von Instanzen dieses Modells mit einem Mehrfachauswahl-Widget für das Listenfeld zu verwenden. Daher habe ich einige benutzerdefinierte Klassen wie folgt erstellt

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

Diese Klassen ermöglichen es, das Listenfeld im Adminbereich zu verwenden. Dann habe ich ein Formular erstellt, das auf der Admin-Site verwendet werden kann

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

Nachdem ich dies getan hatte, erstellte ich ein Admin-Modell und registrierte es auf der Admin-Site

class MyClassAdmin(admin.ModelAdmin):
    form = MyClassForm

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

admin.site.register(MyClass, MyClassAdmin)

Dies funktioniert jetzt in meinem Code. Beachten Sie, dass dieser Ansatz möglicherweise überhaupt nicht gut für google_appengine geeignet ist, da ich nicht sehr versiert darin bin, wie es funktioniert, und es könnte ineffiziente Abfragen und dergleichen erzeugen.

3voto

Can Bascil Punkte 938

Soweit ich verstanden habe, versuchen Sie, eine M2M-Beziehung in django-nonrel zu haben, was keine Out-of-the-Box-Funktionalität ist. Für den Anfang, wenn Sie einen schnellen Hack wollen, können Sie mit dieser einfachen Klasse gehen und verwenden Sie eine CharField um Fremdschlüssel manuell einzugeben:

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]

Aber wenn Sie eine Mehrfachauswahl aus einer Liste von Modellen haben wollen, müssten Sie normalerweise ModelMultipleChoiceField verwenden, was in django-nonrel auch nicht funktioniert. Hier ist, was ich getan habe, um eine M2M-Beziehung mit einem MultipleSelectField zu emulieren:

Angenommen, Sie haben eine M2M-Beziehung zwischen zwei Klassen, SomeClass und AnotherClass. Sie möchten die Beziehung auf dem Formular für SomeClass auswählen. Außerdem nehme ich an, dass Sie die Referenzen als ListField in SomeClass speichern möchten. (Natürlich wollen Sie M2M-Beziehungen so erstellen, wie sie erklärt werden aquí , um explodierende Indizes zu verhindern, wenn Sie mit App Engine arbeiten).

Sie haben also Ihre Modelle wie:

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

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

Und in Ihrem Formular:

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

Hoffentlich sollte dies Sie für den Anfang, ich implementiert diese Funktionalität für normale Formulare, passen Sie es für Admin-Panel sollte nicht so schwer sein.

0voto

jonincanada Punkte 409

Dies könnte unabhängig sein, aber für die Admin-Schnittstelle, stellen Sie sicher, dass Sie djangotoolbox nach django.contrib.admin in den Einstellungen aufgeführt haben. INSTALLED_APPS

0voto

Saurabh Punkte 69

Sie könnten eine benutzerdefinierte Formularklasse für eine solche Verwendung vermeiden, indem Sie nach dem Modellobjekt fragen

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X