2009-12-11 28 views
5

Tôi đang tạo một ứng dụng Django sử dụng một số thừa kế trong mô hình của nó, chủ yếu là vì tôi cần gán tất cả mọi thứ cho UUID và tham chiếu để tôi biết lớp đó là gì. Dưới đây là một phiên bản đơn giản của lớp cơ sở:Thừa kế và chức năng nhà máy trong Python và Django

class BaseElement(models.Model): 
    uuid = models.CharField(max_length=64, editable=False, blank=True, default=lambda:unicode(uuid4())) 
    objmodule = models.CharField(max_length=255, editable=False, blank=False) 
    objclass = models.CharField(max_length=255, editable=False, blank=False) 

class ChildElement(BaseElement): 
    somefield = models.CharField(max_length=255) 

Tôi muốn chắc chắn rằng objmodule, objclass, và uuid được thiết lập tự động. Tôi đã học được từ this post rằng đó là một ý tưởng tồi để làm điều đó bằng cách viết constructor của riêng tôi, và rằng tôi tốt hơn off viết một chức năng nhà máy. Vì vậy, bây giờ BaseElement và ChildElement của tôi trông giống như sau:

class BaseElement(models.Model): 
    uuid = models.CharField(max_length=64, editable=False, blank=True, default=lambda:unicode(uuid4())) 
    objmodule = models.CharField(max_length=255, editable=False, blank=False) 
    objclass = models.CharField(max_length=255, editable=False, blank=False) 

    def set_defaults(self): 
     self.objmodule = unicode(self.__class__.__module__) 
     self.objclass = unicode(self.__class__.__name__) 
     self.uuid = unicode(uuid4()) 

class ChildElement(BaseElement): 
    somefield = models.CharField(max_length=255) 

    @staticmethod 
    def create(*args, **kwargs): 
     ce = ChildElement(*args, **kwargs) 
     ce.set_defaults() 
     return ce 

Tác phẩm này. Tôi có thể gọi ChildElement.create(somefield="foo") và tôi sẽ nhận được một đối tượng thích hợp với các trường uuid, objmoduleobjclass được đặt chính xác. Tuy nhiên, khi tôi đi qua và tạo thêm các lớp như ChildElement2ChildElement3, tôi thấy rằng tôi đang chèn chính xác cùng một chức năng nhà máy tĩnh. Điều này làm phiền tôi vì mã trùng lặp là xấu.

Với phương pháp thông thường, tôi chỉ cần chèn hàm nhà máy create vào BaseElement, tuy nhiên, tôi không thể làm điều đó ở đây vì tôi không có tay cầm (vì chưa được tạo) để nhận thông tin về lớp của đối tượng đã gọi phương thức.

Có cách nào mà tôi có thể di chuyển nhà máy này vào lớp BaseElement vì vậy tôi không cần phải sao chép mã này ở khắp mọi nơi và vẫn có nó để nó tự động cài đặt các giá trị của uuid, objmodule, và objclass?

Trả lời

7

Nếu bạn thực hiện create() một @classmethod thay vì @staticmethod, bạn sẽ được tiếp cận với các đối tượng lớp, mà bạn có thể sử dụng thay vì đề cập đến nó theo tên:

@classmethod 
def create(cls, *args, **kwargs): 
    obj = cls(*args, **kwargs) 
    obj.set_defaults() 
    return obj 

này hiện chung chung và có thể đi trên lớp cơ sở thay vì mỗi lớp con.

+1

Đó chính xác là những gì tôi đang tìm kiếm. Cảm ơn bạn đã nhắc tôi về '@ classmethod'. Nó không phải cái gì tôi sử dụng rất thường xuyên. – Pridkett

+0

Trong Python, hiếm khi có trường hợp sử dụng tốt cho '@ staticmethod' - đó là trường hợp mà bạn có thể nên sử dụng ít thường xuyên hơn, nếu có. Thường thì nếu một cái gì đó không phải là một '@ classmethod' nó là thích hợp hơn như là một chức năng cấp cao nhất đơn giản trong một mô-đun. –

2

Tôi nghĩ rằng bạn có thể đã cố gắng ghi đè tốt hơn trong BaseElement của mình. Sau đó, khi lưu bạn có thể thiết lập các trường đó. Nó sẽ là một cái gì đó như:

class MyBase(models.Model): 
    uuid = models.CharField(max_length=64, editable=False, blank=True, 
     default=lambda:unicode(uuid4())) 
    objmodule = models.CharField(max_length=255, editable=False, blank=False) 
    objclass = models.CharField(max_length=255, editable=False, blank=False) 

    def save(self): 
     if not self.id: 
      self.objmodule = unicode(self.__class__.__module__) 
      self.objclass = unicode(self.__class__.__name__) 
      self.uuid = unicode(uuid4()) 
     super(self.__class__.__base__, self).save() 

class InheritedFromBase(MyBase): 
    new_field = models.CharField(max_length=100) 

Tôi đã thử nghiệm với điều đó và nó dường như làm những gì bạn đang tìm kiếm. Tôi đã có thể tạo một đối tượng "InheritedFromBase" có các trường bạn cần, mà không có nhiều bản sao mã.

+0

+1: Chỉ cần ghi đè lên 'lưu' gần như luôn là điều đúng đắn cần làm. –

+1

Đó là gần, và có thể chứng minh được chấp nhận được, nhưng không hoàn toàn là những gì tôi cần. Ngay bây giờ tôi tạo một số đối tượng trên máy chủ, sau đó tuần tự hóa chúng cho một máy khách để sử dụng trong một ứng dụng AJAXy. Máy khách có thể sửa đổi các thuộc tính và sau đó lưu các đối tượng. Với thiết lập của bạn, họ sẽ không nhận được 'uuid',' objmodule' và 'objclass' được gán cho đến khi chúng được lưu. Khi tôi sử dụng 'uuid' trên máy khách, có lẽ tôi sẽ cần thêm một số logic bổ sung vào máy chủ, hoặc có máy khách gán nó. Rất gần, nhưng tôi muốn tìm cách mà không cần gọi 'save' trước. Tôi có thể giải quyết nếu điều này là đúng cách, tuy nhiên. – Pridkett