2016-07-01 5 views
6

Ich versuche, ein generisches Mixin für Modellfelder (im Gegensatz zu Formularfeldern) zu machen, die Init für das Mixin nimmt benannte Argumente. Ich stehe in Schwierigkeiten, das Mixen mit einer anderen Klasse zu erstellen.So erstellen Sie ein Django-Modell-Feld mixin

Hier ist der Code

class MyMixin(object): 
    def __init__(self, new_arg=None, *args, **kwargs): 
     super(MyMixin, self).__init__(*args, **kwargs) 
     print self.__class__, new_arg 


class MyMixinCharField(MyMixin, models.CharField): 
    pass 

... 

class MyMixinModelTest(models.Model): 
    myfield = MyMixinCharField(max_length=512,new_arg="myarg") 

die Migration für dieses Modell machen die folgende Ausgabe:

<class 'myapp.mixintest.fields.MyMixinCharField'> myarg 
<class 'myapp.mixintest.fields.MyMixinCharField'> None 
<class 'myapp.mixintest.fields.MyMixinCharField'> None 
Migrations for 'mixintest': 
    0001_initial.py: 
     - Create model MyMixinModelTest 

Erstens, warum init 3 mal ausgeführt wird? Wo ist der Kwarg 'New_arg' in den zweiten beiden? Wie erstelle ich ein Feld Mixin für Django?

EDIT: Was another question Gegensatz diese Frage zu Feld Mixins fragt, verweist die verknüpfte Frage Modell Mixins.

+0

Welche Version von Django verwenden Sie? – Aya

+0

Ich benutze Version 1.9 – jmerkow

+0

@solarissmoke es ist nicht die gleiche Frage. –

Antwort

2

Erstens, warum init 3 mal ausgeführt wird?

Obwohl die models.py nur einmal importiert, die Field Objekte erstellt darin, wie ...

myfield = MyMixinCharField(max_length=512, new_arg="myarg") 

... mehrfach geklont, was beinhaltet das Feld Konstruktor aufrufen das Schlüsselwort args mit Sie wurden ursprünglich mit erstellt. Sie können die traceback Modul zu sehen, benutzen, wo es passiert ...

import traceback 

class MyMixin(object): 
    def __init__(self, new_arg=None, *args, **kwargs): 
     super(MyMixin, self).__init__(*args, **kwargs) 
     print self.__class__, new_arg 
     traceback.print_stack() 

..., die die folgenden mehrmals in der Ausgabe zeigt, ...

File "django/db/migrations/state.py", line 393, in from_model 
    fields.append((name, field.clone())) 
    File "django/db/models/fields/__init__.py", line 464, in clone 
    return self.__class__(*args, **kwargs) 
    File "myproj/myapp/models.py", line 11, in __init__ 
    traceback.print_stack() 

Wo liegt der Kwarg 'New_arg' in den zweiten beiden?

Wenn Sie ursprünglich ... genannt als new_arg Parameter übergeben zu ...

def __init__(self, new_arg=None, *args, **kwargs): 

... sondern weil Sie

myfield = MyMixinCharField(max_length=512, new_arg="myarg") 

... "myarg" wird don Übergeben Sie diesen Parameter nicht an den zugrunde liegenden Field Konstruktor ...

super(MyMixin, self).__init__(*args, **kwargs) 

...Es wird nicht irgendwo im zugrunde liegenden Field-Objekt gespeichert. Wenn das Feld geklont wird, wird der Parameter new_arg nicht an den Konstruktor übergeben.

jedoch zum Oberklassenkonstruktors diese Option geben wird nicht funktionieren, weil die CharField nicht, dass Schlüsselwort arg unterstützen, so dass Sie bekommen ...

File "myproj/myapp/models.py", line 29, in MyMixinModelTest 
    myfield = MyMixinCharField(max_length=512, new_arg="myarg") 
    File "myproj/myapp/models.py", line 25, in __init__ 
    super(MyMixinCharField, self).__init__(*args, **kwargs) 
    File "django/db/models/fields/__init__.py", line 1072, in __init__ 
    super(CharField, self).__init__(*args, **kwargs) 
TypeError: __init__() got an unexpected keyword argument 'new_arg' 

Wie erstelle ich ein Feld Mixin für Django?

Aufgrund dieses Klonen Verhalten, wenn Sie benutzerdefinierte Feld Optionen hinzufügen möchten, müssen Sie eine benutzerdefinierte deconstruct() Methode definieren, so dass Django Ihre neue Option ...

class MyMixin(object): 
    def __init__(self, new_arg=None, *args, **kwargs): 
     super(MyMixin, self).__init__(*args, **kwargs) 
     self.new_arg = new_arg 
     print self.__class__, new_arg 

    def deconstruct(self): 
     name, path, args, kwargs = super(MyMixin, self).deconstruct() 
     kwargs['new_arg'] = self.new_arg 
     return name, path, args, kwargs 


class MyMixinCharField(MyMixin, models.CharField): 
    pass 


class MyMixinModelTest(models.Model): 
    myfield = MyMixinCharField(max_length=512, new_arg="myarg") 

Serialisierung kann ... welche Ausgänge ...

<class 'myapp.models.MyMixinCharField'> myarg 
<class 'myapp.models.MyMixinCharField'> myarg 
<class 'myapp.models.MyMixinCharField'> myarg 
+0

Ich habe nicht alle meine Fragen in meiner Antwort beantwortet, du hast es getan. Also werde ich deine annehmen. – jmerkow

+0

Schnelle Frage, in meiner Antwort habe ich MyMixin von models.Field geerbt, Hat das Auswirkungen auf irgendetwas? – jmerkow

+0

@jmerkow Es ist keine besonders schnelle Frage, die aufgrund der Verwendung von Metaklassen in 'Model' definitiv beantwortet werden kann. Wenn 'MyMixin' Unterklassen' Field' direkt, dann können unter gewissen Umständen Funktionen in 'CharField', die die in' Field' definierten überschreiben, ignoriert werden, und es wird am Ende die falsche Funktion aufrufen. Es ist wahrscheinlich sicherer, diese Möglichkeit zu vermeiden und nur die Unterklasse 'MyMixin'' object' zu verwenden, was Mixins ohnehin tun sollten. – Aya

1

Also habe ich es nach viel Basteln und erneutes Lesen der django docs on custom model fields herausgefunden Sie brauchen einen Dekonstruktor zusammen mit Ihrer init. Django-Felder benötigen eine deconstruct-Methode zum Serialisieren.

Die mixin sollten diese Methode haben auch:

class MyMixin(object): 
def __init__(self, new_arg=None, *args, **kwargs): 
    self.new_arg = new_arg 
    super(MyMixin, self).__init__(*args, **kwargs) 

def deconstruct(self): 
    name, path, args, kwargs = super(MyMixin, self).deconstruct() 
    if self.new_arg is not None: 
     kwargs['new_arg'] = self.new_arg 
    return name, path, args, kwargs 
+0

Ich war fast fertig, eine ausführlichere Antwort zu schreiben, als du das gepostet hast. :) – Aya