Ich habe meinen eigenen ORM-Mapper erstellt, aber den Prozess der Übergabe des Vergleichsoperators als Argument an die where-Methode implementiert. Es wäre schön, wenn Sie sich vorstellen könnten, wie man es wie folgt benutzt. Sie können jede Bedingung der where-Klausel intuitiver angeben.
Session.select('coulmn_name1', 'coulmn_name2').where(Model.id > 1, _and, Model.name == "Mike")
Wenn Sie "Model.id> 1" als Argument übergeben, wird normalerweise "True" oder "False" übergeben, und "** id" bedeutet, dass mehr als 1 ** nicht übergeben werden kann. Wenn Sie jedoch die spezielle Methode neu schreiben, können Sie das Argument übergeben, ohne es als Vergleichsoperator zu verarbeiten.
In dieser where-Methode wird nur der Prozess "Zurückgeben der where-Klausel als Zeichenfolge" implementiert. Es ist einfach zu tun, aber diese Implementierung zu kennen, erwies sich als nützlich für die Metaprogrammierung.
Ich denke, dass nützliche Informationen für solche Menschen erhalten werden können. In diesem Artikel wird die Metaprogrammierung selbst jedoch nicht erläutert. Wenn Sie sich mit Metaprogrammierung auskennen und ein Beispiel sehen möchten, hilft Ihnen dieser Artikel.
Wenn Sie eine Tabelle haben, die Personen mit dem Namen Person behandelt, deklarieren Sie das Personenmodell in der Klasse wie folgt.
from model_base import *
class Person(ModelBase):
id = {'default': 1}
name = {'default': 'Mike'}
Sie müssen lediglich die erforderlichen Spaltennamen in Ihrem Modell deklarieren. Geben Sie für den Anfangswert die Option im Wörterbuch an. Diesmal haben wir nur die Defalut-Option. Solange Sie ModelBase erben, wird das Modell automatisch zusammengestellt.
Wenn die Person-Klasse geladen wird, enthalten die Klassenfelder "id" und "name" keine Wörterbücher wie "{'default': 1}". Das Spaltenklassenobjekt wird automatisch von der Metaklasse eingeschlossen. Wenn Sie "default" sehen möchten, schreiben Sie einfach "pseron_instance.id.default".
Deklarieren Sie den Spaltennamen ohne _ (Unterstrich) am Anfang des Klassenfelds. Wenn Sie _ (Unterstrich) als Präfix verwenden, wird es nicht als Spalte erkannt. Stellen Sie daher der erforderlichen Methode oder dem erforderlichen Feld _ (Unterstrich) als privat voran.
Diesmal gibt die where-Methode die zusammengesetzte where-Klausel als Zeichenfolge zurück.
from person import *
if __name__ == '__main__':
db_manager = DBManager()
#Zusammenstellung der where-Klausel
db_manager.where(Person.id > 1, "or", Person.name == 'Mike')
#Einfach einzugeben, wenn mit lokalen Variablen vorbereitet
_and = "and"
_or = "or"
db_manager.where(Person.id > 1, _and, Person.name == 'Mike')
#Überprüfen Sie die Standardeinstellung
person_A = Person()
print("person_A.id = %s" % person_A.id)
print("person_A.name = %s" % person_A.name)
# Model.Colm-Objekt für ID, Modell.Fehler, da id den Spaltenwert enthält
person_A = "Jane"
print(Person.name.default)
# print(person_A.name.default)
# => AttributeError: 'str' object has no attribute 'name'
Es sind vier Klassen vorzubereiten.
class Column():
def __init__(self, column_name, dict):
#Halten Sie Spaltennamen in Klassenvariablen
self.column_name = column_name
#Wird beim Empfang des Einstellwerts jeder Spalte festgelegt
if dict is not None:
for key, value in dict.items():
if key == "default":
self.default = value
def __setattr__(self, key, value):
#Sie können den Spaltentyp überprüfen, indem Sie den Wert überprüfen und einen Fehler auslösen.
self.__dict__[key] = value
#Überlastvergleichsoperator
def __eq__(self, other):
return "%s = %s" % (self.column_name, other)
def __ne__(self, other):
return "%s != %s" % (self.column_name, other)
def __lt__(self, other):
return "%s < %s" % (self.column_name, other)
def __gt__(self, other):
return "%s > %s" % (self.column_name, other)
def __le__(self, other):
return "%s <= %s" % (self.column_name, other)
def __ge__(self, other):
return "%s >= %s" % (self.column_name, other)
class MetaModel(type):
def __new__(cls, cls_name, cls_bases, cls_dict):
for key, value in cls_dict.items():
#Extrahieren Sie nur öffentliche Attribute
if not(key.startswith("_")):
#Säule(Modellklassenvariablen)Kann den Anfangswert von abrufen
#Halten Sie den Variablennamen im Spaltenfeld
cls_dict[key] = Column(key, value)
return super().__new__(cls, cls_name, cls_bases, cls_dict)
class ModelBase(metaclass=MetaModel):
#Wird sofort nach dem Erstellen einer Instanz aufgerufen
def __init__(self):
#Legen Sie den Standardwert für das Column-Objekt fest
class_dict = self.__class__.__dict__
for column_key, column_value in class_dict.items():
#Extrahieren Sie nur öffentliche Attribute
if not(column_key.startswith("_")):
setattr(self, column_key, column_value.default)
class DBManager():
def where(self, *args):
statement = "WHERE"
for arg in args:
if arg.upper() in ["AND", "OR"]:
statement += " %s " % arg.upper()
else:
statement += " %s " % arg
print(statement)
Ich habe die Metaklasse auf ModelBase gesetzt. Dies ist so, dass Sie nur "MetaModel" schreiben müssen, anstatt "metaclass = MetaModel" in die Unterklasse zu schreiben. Zusätzlich wird hier die gemeinsame Verarbeitung des Modells beschrieben.
Informationen für jede Spalte werden von einer Instanz einer dedizierten Klasse verwaltet. Auf diese Weise können Sie eine automatische Verarbeitung wie die Validierung für jede Spalte implementieren. Wenn es keine dedizierte Klasse gibt, Spaltenklasse, können Sie nur Spaltenwerte übergeben.
#Ohne ModelBase
class MyModel(metaclass=MetaModel):
pass
#Wenn Sie ModelBase haben
class MyModel(ModelBase):
pass
Selbst wenn Sie Person nicht instanziieren, wird ModelMeta, die Metaklasse von Person, ausgeführt, wenn Person geladen wird. Daher wird ein Spaltenobjekt (eine Instanz) dynamisch erstellt und in der ID und dem Namen der Klassenfelder der Person festgelegt. Diese Spalte enthält den Standardwert von jedem in der Standardeinstellung des Instanzmitglieds.
{" default ": 1}
Eine Metaklasse namens MetaModel kann auch als reguläre Klasse definiert werden, anstatt als Metaklasse. In diesem Fall können Sie einfach ModelBase verwenden. In diesem Fall müssen Sie jedoch einmal eine Instanz erstellen. Ich denke, Sie sollten die Instanziierung ganz am Ende des Quellcodes schreiben, damit sie beim Laden des Moduls automatisch ausgeführt wird. Aber ich denke nicht, dass es eine gute Idee ist.
class Column():
pass
class MetaModel(type):
pass
class DBManager():
pass
class ModelBase():
pass
ModelBase()`
Der Grund, warum Sie eine Instanz einmal erstellen müssen, ist, dass "new" den Vorgang zum Erstellen einer Instanz ausführt. (Streng kurz zuvor. Ich verlasse die Generation separat.) Wichtig ist hier, um welche Art von Instanz es sich handelt. Eine Metaklasse ist eine Klasse zum Erstellen einer Klasse, dh einer Instanz der Metaklasse. Andererseits ist eine einfache Klasseninstanz ein Objekt, das aus einer normalen Klasse erstellt wurde. Normalerweise ist eine Instanz ein Produkt einer Klasse, aber wenn wir über Beziehungen sprechen, können wir die Ausdrucksinstanz in Metaklassen und Klassen verwenden.
Die folgenden zwei Beziehungen werden beide mit der Blaupause generiert. Die rechte Seite kann als Instanz angesehen werden.
Wenn Sie in diesem Sinne sehen, dass "new" das ist, was Sie tun, wenn Sie eine Instanz erstellen, ist der einfache Unterschied zwischen einer Metaklasse und einer Klasse "new" die Reihenfolge der Verarbeitung. Organisieren Sie den letzten auszuführenden Ausführungsauftrag.
** Ausführungsreihenfolge **
Metaclass .__ new __
Klasse .__ neu __
Klasse .__ init __