In the python metaclass, pass the comparison operator as a string to the ORM method to assemble the where clause.

I made my own ORM mapper, but I implemented the process of passing the comparison operator as an argument to the where method. It would be nice if you could imagine how to use it as follows. You can specify each condition of the where clause more intuitively.

Session.select('coulmn_name1', 'coulmn_name2').where(Model.id > 1, _and, Model.name == "Mike")

If Model.id> 1, is passed as an argument, True or False is usually passed, and ** id means greater than 1 ** cannot be passed. However, if you rewrite the special method, you can pass the argument without processing it as a comparison operator.

In this where method, only the process of "returning the where clause as a character string" is implemented. It's simple to do, but knowing this implementation proved useful for metaprogramming.

I think that useful information can be obtained for such people. However, I will not explain metaprogramming itself in this article. If you know metaprogramming and want to see an example, this article will help you.

How to make your own model

If you have a table that handles a person called Person, declare the Person model in the class as follows.

from model_base import *

class Person(ModelBase):
    id = {'default': 1}
    name = {'default': 'Mike'}

All you have to do is declare the required column names in your model. For the initial value, specify the option in the dictionary. This time we only have the defalut option. As long as you inherit ModelBase, the model will be assembled automatically.

When the Person class is loaded, the class fields, ʻid and name, do not contain dictionaries like {'default': 1}. The column class object is automatically included by the metaclass. If you want to see default, just write pseron_instance.id.default`.

Variable name treated as a column name

Declare the column name without _ (underscore) at the beginning of the class field. If you use the prefix _ (underscore), it will not be recognized as a column, so please prefix the required methods and fields with _ (underscore) as private.

Actually use

This time, the where method returns the assembled where clause as a string.

from person import *

if __name__ == '__main__':
    db_manager = DBManager()

    #Where clause assembly
    db_manager.where(Person.id > 1, "or", Person.name == 'Mike')
    #Easy to type if prepared with local variables
    _and = "and"
    _or = "or"
    db_manager.where(Person.id > 1, _and, Person.name == 'Mike')

    #Check the default
    person_A = Person()
    print("person_A.id = %s" % person_A.id)
    print("person_A.name = %s" % person_A.name)

    # Model.Coulm object for id, model.Error because id contains column value
    person_A = "Jane"
    print(Person.name.default)
    # print(person_A.name.default)
    # => AttributeError: 'str' object has no attribute 'name'

Module to assemble the model

There are four classes to prepare.

class Column():

    def __init__(self, column_name, dict):
        #Hold the column name in a class variable
        self.column_name = column_name

        #Set when receiving the setting value of each column
        if dict is not None:
            for key, value in dict.items():
                if key == "default":
                    self.default = value

    def __setattr__(self, key, value):
        #You can check the column type by checking the value and throwing an error
        self.__dict__[key] = value

    #Overload comparison operator
    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():
            #Extract only public attributes
            if not(key.startswith("_")):
                #column(Model class variables)Can retrieve the initial value of
                #Hold variable name in Column field
                cls_dict[key] = Column(key, value)
        return super().__new__(cls, cls_name, cls_bases, cls_dict)

class ModelBase(metaclass=MetaModel):
    #Called immediately after instantiating
    def __init__(self):
        #Set the default value set for the Column object
        class_dict = self.__class__.__dict__
        for column_key, column_value in class_dict.items():
            #Extract only public attributes
            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)

I set the metaclass to ModelBase. This is so that you don't have to write metaclass = MetaModel in the subclass, just write MetaModel. In addition, the common processing of the model is described here.

The information of each column is managed by the instance by preparing a dedicated class. By doing this, you can implement automatic processing such as validation on each column. If there is no dedicated class, Column class, only column values can be passed.

#Without ModelBase
class MyModel(metaclass=MetaModel):
    pass

#If you have ModelBase
class MyModel(ModelBase):
    pass

Even if you do not instantiate the Person, ModelMeta, which is the Person's metaclass, will be executed when the Person is loaded. Therefore, a Column object (instance) is dynamically created and set in the id and name of Person's class fields. This Column holds the default value of each in the default of the instance member.

Flow that Column object is set in the class field of Person

  1. Load the Person class
  2. MetaModel is executed
  3. Receive the expected initial value of the Person's class field Example: {"default ": 1}
  4. Create a Column object with {"default": 1} as an argument
  5. Register Column object in dict which is a list of class attributes
  6. Let the metaclass type, the parent of the MetaModel, define the class fields
  7. Set a Column object with metaclass type to Person's class field

Difference between metaclass and class new

A metaclass called MetaModel can also be defined as a regular class instead of being a metaclass. In that case, you could just use ModelBase. However, in that case, you have to create an instance once. I think you should write the instantiation at the very end of the source code so that it will be executed automatically when the module is loaded. But I don't think it's a good idea.

class Column():
    pass

class MetaModel(type):
    pass

class DBManager():
    pass

class ModelBase():
	pass

ModelBase()`

The reason why you need to create an instance once is that __new__ does the process of creating an instance. (Strictly speaking, it's just before. I'll leave the generation to you.) The important thing here is what instance it is. A metaclass is a class for creating a class, that is, an instance of the metaclass. On the other hand, just an instance of a class is an object created from a class you normally use. Usually, an instance is a product of a class, but when we talk about relationships, we can use the expression instance in a metaclass and a class.

The following two relationships are both generated with the blueprint. The right side can be seen as an instance.

With this in mind, if you see that __new__ is what you do when you instantiate, the simple difference between a metaclass and a class __new__ is the order of processing. Organize the last execution order to finish.

** Order of execution **

  1. Metaclass .__ new __
  2. The class is created
  3. Class .__ new __
  4. An instance is created
  5. Class .__ init__
  6. Instance variables are set

Recommended Posts

In the python metaclass, pass the comparison operator as a string to the ORM method to assemble the where clause.
How to use the __call__ method in a Python class
[Introduction to Python] How to use the in operator in a for statement?
Get the formula in an excel file as a string in Python
The eval () function that calculates a string as an expression in python
How to input a character string in Python and output it as it is or in the opposite direction.
How to embed a variable in a python string
To dynamically replace the next method in python
How to pass the execution result of a shell command in a list in Python
[Python] I tried to get the type name as a string from the type function
Check if the string is a number in python
Parse a JSON string written to a file in Python
How to convert / restore a string with [] in python
I want to embed a variable in a Python string
[Python] How to expand variables in a character string
[Python] Created a method to convert radix in 1 second
[C / C ++] Pass the value calculated in C / C ++ to a python function to execute the process, and use that value in C / C ++.
[Python] Programming to find the number of a in a character string that repeats a specified number of times.
How to quickly count the frequency of appearance of characters from a character string in Python?
How to pass the execution result of a shell command in a list in Python (non-blocking version)
Find out the apparent width of a string in python
Change the standard output destination to a file in Python
I tried "How to get a method decorated in Python"
How to generate a query using the IN operator in Django
In Django, how to abbreviate the long displayed string as ....
"A book to train programming skills to fight in the world" Python code Solution example --1.6 String compression
Run the output code on the local web server as "A, pretending to be B" in python
[Python] Create a program to delete line breaks in the clipboard + Register as a shortcut with windows
How to determine the existence of a selenium element in Python
How to pass arguments to a Python script in SPSS Modeler Batch
How to make a string into an array or an array into a string in Python
[Introduction to Python] How to split a character string with the split function
[Introduction to Python] How to output a character string in a Print statement
How to check the memory size of a variable in Python
python: Use the variables defined in the string format as they are
How to get a string from a command line argument in python
Timezone specification when converting a string to datetime type in python
How to check the memory size of a dictionary in Python
A function that measures the processing time of a method in python
In the python command python points to python3.8
4 methods to count the number of occurrences of integers in a certain interval (including imos method) [Python implementation]
6 ways to string objects in Python
Create a Kubernetes Operator in Python
Create a random string in Python
If you want a singleton in python, think of the module as a singleton
Note: [Python3] Convert datetime to a string in any format you like
How to install python package in local environment as a general user
I want to batch convert the result of "string" .split () in Python
I want to color a part of an Excel string in Python
How to write a string when there are multiple lines in python
[Introduction to Python] How to write a character string with the format function
[Introduction to Python] Thorough explanation of the character string type used in Python!
I made a program to check the size of a file in Python
What is the fastest way to create a reverse dictionary in python?
How to sort by specifying a column in the Python Numpy array.
Move the turtle to the place where you click the mouse with turtle in Python
<Python> A quiz to batch convert file names separated by a specific character string as part of the file name
Method to build Python environment in Xcode 6
Write the test in a python docstring
[Python] How to invert a character string
Generate a class from a string in Python