2014-01-23 4 views
1

В моем проекте Django я хочу, чтобы все мои поля модели имели дополнительный аргумент, называемый documentation. (Это было бы похоже на verbose_name или help_text, но вместо внутренней документации.)Переопределить __init__ для всех классов в модуле

Это кажется простым: просто подкласс и переопределить это месторождение __init__:

def __init__(self, verbose_name=None, name=None, documentation=None, **kwargs): 
    self.documentation = documentation 
    super(..., self).__init__(verbose_name, name, **kwargs) 

Вопрос в том, как я делаю это относится к все из 20 полевых классов в django.db.models (BooleanField, CharField, PositiveIntegerField и т. Д.)?

Единственный способ я вижу использовать метапрограммирование с инспектировать модуля:

import inspect 
import sys 
from django.db.models import * 

current_module = sys.modules[__name__] 

all_field_classes = [Cls for (_, Cls) in inspect.getmembers(current_module, 
      lambda m: inspect.isclass(m) and issubclass(m, Field))] 

for Cls in all_field_classes: 
    Cls.__init__ = <???> 

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

Любые идеи?

+0

насчет перезаписи класса 'Field'? –

+0

@Raydel - он не будет работать, если все его подклассы тщательно обработаны, чтобы не заботиться о аргументах, которые не относятся к подклассу, и вызывают '__init__' суперкласса с неизвестным, дополнительным аргументом. Вы можете создать систему, чтобы вести себя так, но вы не можете полагаться на работу других, чтобы сделать это. (Хотя можно посмотреть код soruce и посмотреть, что произойдет) – jsbueno

+0

@RaydelMiranda: Вы имеете в виду подкласс «Поле» в моем коде? Я не думаю, что это повлияло бы на поведение подклассов «Поле». Это будет просто создание другого подкласса. – RexE

ответ

1

Действительно - вы на правильном пути. В питона, самоанализ нормальная вещь, и, и вы не должны даже использовать inspect модуль только потому, что «я использую самоанализ и мета-программирования, я должен нуждаться в inspect) :-)

Одна вещь, которая не является что большая часть хорошей практики, однако, является исправлением Monkey - то есть, если вы меняете классы, как они есть в самом django.db.models, так что другие модули будут импортировать измененные классы оттуда и использовать модифицированную версию. (Обратите внимание, что в этом случае: не рекомендуется! = не будет работать), поэтому вам лучше создать все новые классы моделей в вашем собственном модуле и импортировать их из собственного модуля, а не от django.db.models

Так, что-то вместе:

from django.db import models 

# A decorator to implement the behavior you want for the 
# __init__ method 
def new_init(func): 
    def __init__(self, *args, **kw): 
     self.documentation = kw.pop("documentation", None) 
     return func(self, *args, **kw) 

for name, obj in models.__dict__.items(): 
    #check if obj is a class: 
    if not isinstance(obj, type): 
     continue 

    # creates a new_init, retrieving the original one - 
    # taking care for not to pick it as an unbound method - 
    # check: http://pastebin.com/t1SAusPS 
    new_init_method = new_init(obj.__dict__.get("__init__", lambda s:None)) 

    # dynamically creates a new sublass of obj, overriding just the __init__ method: 
    new_class = type(name, (obj,), {"__init__": new_init_method}) 

    # binds the new class to this module's scope: 
    globals().__setitem__(name, new_class) 

Или, если вы предпочитаете использовать обезьяну заплатки, как это проще :-p

from django.db import models 

def new_init(func): 
    def __init__(self, *args, **kw): 
     self.documentation = kw.pop("documentation", None) 
     return func(self, *args, **kw) 

for name, obj in models.__dict__.items(): 
    #check if obj is a class: 
    if not isinstance(obj, type): 
     continue 

    obj.__init__ = new_init(obj.__dict__["__init__"]) 
+0

спасибо !! Очень полезно. – RexE

Смежные вопросы