2014-10-20 159 views
0

我有一個項目,我在加密文本字段中存儲外鍵值。這樣做的目的是將表格分成兩組,一組具有個人身份信息,另一組沒有。減少重複的Django模型代碼

class User(AbstractBaseUser): 
    # ... fields 

    encrypted_identification_id = models.TextField(null=True) 
    encrypted_identification_key = models.TextField(null=True) 

    def get_identification(self, private_key): 
     if not self.encrypted_identification_key: 
      return None 
     identification_id = decrypt(private_key, self.encrypted_identification_id, self.encrypted_identification_key) 
     return Identification.objects.get(pk=identification_id) 

    def set_identification(self, identification): 
     encrypted = encrypt(str(identification.pk)) 
     self.encrypted_identification_id = encrypted['encrypted_string'] 
     self.encrypted_identification_key = encrypted['aes_key'] 
     self.save() 


class Identification(models.Model): 
    # ... fields 

    encrypted_user_id = models.TextField(null=True) 
    encrypted_user_key = models.TextField(null=True) 

    def get_user(self, private_key): 
     if not self.encrypted_user_key: 
      return None 
     user_id = decrypt(private_key, self.encrypted_user_id, self.encrypted_user_key) 
     return User.objects.get(pk=user_id) 

    def set_user(self, user): 
     encrypted = encrypt(str(user.pk)) 
     self.encrypted_user_id = encrypted['encrypted_string'] 
     self.encrypted_user_key = encrypted['aes_key'] 
     self.save() 

的代碼字段和方法的不同之處在於他們有和使用不同的名稱相同的:每個帶有該功能模型2場和2種方法實現它。我有很多像這樣的模型,都複製並粘貼了看起來像這樣的代碼。減少這種重複的最有效方法是什麼?

回答

0

Ben的答案可以減少方法中重複的代碼,但對重複的字段沒有任何作用。

一個更好的方法是使用元類來創建所需的字段和方法:

class EncryptedRelationModelBase(ModelBase): 
    related_model = None 
    app_label = None 
    model_name = None 
    added_encrypted_attrs = False 

    def __new__(cls, name, bases, attrs): 
     encrypted_attrs = cls.get_encrypted_attrs() 
     attrs.update(encrypted_attrs) 
     return super(EncryptedRelationModelBase, cls).__new__(cls, name, bases, 
                   attrs) 

    @classmethod 
    def get_encrypted_attrs(cls): 
     # This method should add attributes only once 
     if cls.added_encrypted_attrs: 
      return {} 

     ret = cls._get_encrypted_attrs() 

     cls.added_encrypted_attrs = True 
     return ret 

    @classmethod 
    def _get_encrypted_attrs(cls): 
     def get_related_model(self, private_key): 
      if not getattr(self, cls.get_key_field_name()): 
       return None 
      related_instance_id = shcrypto.decrypt(
       private_key, 
       getattr(self, cls.get_id_field_name()), 
       getattr(self, cls.get_key_field_name())) 
      return cls.get_related_model().objects.get(pk=related_instance_id) 

     def set_related_model(self, instance): 
      encrypted = encrypt(str(instance.pk)) 
      setattr(self, cls.get_id_field_name(), 
        encrypted['encrypted_string']) 
      setattr(self, cls.get_key_field_name(), encrypted['aes_key']) 
      self.save() 

     attrs = {} 
     attrs[cls.get_id_field_name()] = models.TextField(null=True, blank=True) 
     attrs[cls.get_key_field_name()] = models.TextField(null=True, blank=True) 
     attrs[cls.get_get_method_name()] = get_related_model 
     attrs[cls.get_set_method_name()] = set_related_model 
     return attrs 

    @classmethod 
    def get_related_model(cls): 
     if cls.related_model: 
      return cls.related_model 
     return get_model(cls.app_label, cls.model_name) 

    @classmethod 
    def format_related_model_name(cls): 
     if cls.model_name: 
      return cls.snake_case(cls.model_name) 
     return cls.snake_case(cls.related_model.__name__) 

    @staticmethod 
    def snake_case(name): 
     name = re.sub('(.)([A-Z](?!s[A-Z])[a-z]+)', r'\1_\2', name) 
     name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', name) 
     name = name.lower() 
     name = name.replace('__', '_') 
     return name 

    @classmethod 
    def get_id_field_name(cls): 
     return 'encrypted_{}_id'.format(cls.format_related_model_name()) 

    @classmethod 
    def get_key_field_name(cls): 
     return 'encrypted_{}_key'.format(cls.format_related_model_name()) 

    @classmethod 
    def get_get_method_name(cls): 
     return 'get_{}'.format(cls.format_related_model_name()) 

    @classmethod 
    def get_set_method_name(cls): 
     return 'set_{}'.format(cls.format_related_model_name()) 


def EncryptedRelationModel(related_model=None, app_label=None, model_name=None): 
    def create_metaclass(model, label, name): 
     class NewModelBase(EncryptedRelationModelBase): 
      related_model = model 
      app_label = label 
      model_name = name 
     return NewModelBase 

    class NewModel(models.Model): 
     __metaclass__ = create_metaclass(related_model, app_label, model_name) 

     class Meta: 
      abstract = True 
    return NewModel 


class Identification(UserEncryptedRelationModel(User)): 
    pass 
0

你可以抽象的相關位成一個函數:

def _get_decrypted(id_field, key_field, model, obj, private_key): 
    if not getattr(obj, key_field): 
     return None 
    decrypted_id = decrypt(private_key, getattr(obj, id_field), getattr(obj, key_field) 
    return model.objects.get(pk=decrypted_id) 

,然後使用該函數在每個模型:

def get_identification(self, private_key): 
    return _get_decrypted('encrypted_identification_id', 'encrypted_identification_key', Identification, self, private_key) 

和:

def get_user(self, private_key): 
    return _get_decrypted('encrypted_user_id', 'encrypted_user_key', User, self, private_key) 

而同樣寫函數爲set版本,使用setattr而不是getattr。

你也可以把它變成一個mixin類的方法。

+0

此封裝的方法,而不是字段,以便有什麼意義呢?這些字段仍然必須以聲明方式定義。 – hekevintran 2014-12-27 19:34:04