2010-02-07 36 views
24

Nói rằng tôi có mô hình:Cách đúng để trả về cá thể mô hình proxy từ một cá thể mô hình cơ sở ở Django?

class Animal(models.Model): 
    type = models.CharField(max_length=255) 

class Dog(Animal): 
    def make_sound(self): 
     print "Woof!" 
    class Meta: 
     proxy = True 

class Cat(Animal): 
    def make_sound(self): 
     print "Meow!" 
    class Meta: 
     proxy = True 

Hãy nói rằng tôi muốn làm:

animals = Animal.objects.all() 
for animal in animals: 
    animal.make_sound() 

Tôi muốn lấy lại một loạt các Woofs và Meows. Rõ ràng, tôi có thể chỉ định một make_sound trong mô hình ban đầu mà dĩa dựa trên animal_type, nhưng sau đó mỗi khi tôi thêm một loại động vật mới (tưởng tượng chúng trong các ứng dụng khác nhau), tôi phải đi vào và chỉnh sửa hàm make_sound đó . Tôi chỉ muốn xác định các mô hình proxy và yêu cầu họ xác định hành vi của chính họ. Từ những gì tôi có thể nói, không có cách nào để trả lại các trường hợp Cat hoặc Dog hỗn hợp, nhưng tôi nghĩ có thể tôi có thể định nghĩa phương thức "get_proxy_model" trên lớp chính trả về một con mèo hoặc một mô hình con chó.

Chắc chắn bạn có thể làm điều này, và chuyển một cái gì đó giống như khóa chính và sau đó chỉ cần làm Cat.objects.get (pk = passed_in_primary_key). Nhưng điều đó có nghĩa là làm một truy vấn bổ sung cho dữ liệu bạn đã có mà dường như dư thừa. Có cách nào để biến một con vật thành một con mèo hay một con chó một cách hiệu quả? Cách thích hợp để làm những gì tôi muốn đạt được là gì?

+0

Bạn có thể áp dụng make_sound cho mô hình Động vật và thêm một sound = models.charField() vào nó. – monkut

+2

Ví dụ của tôi là rất đơn giản - những gì tôi đang cố gắng làm đòi hỏi một loạt các công việc phụ thuộc vào loại và không thể được lưu trữ với mô hình. – sotangochips

Trả lời

1

Bạn có thể làm cho mô hình Django đa hình bằng cách sử dụng phương pháp được mô tả here. Mã đó đang ở giai đoạn phát triển ban đầu, tôi tin, nhưng đáng để nghiên cứu.

4

cách duy nhất được biết đến với loại người là sử dụng lập trình Metaclass.

Dưới đây là trả lời ngắn gọn:

from django.db.models.base import ModelBase 

class InheritanceMetaclass(ModelBase): 
    def __call__(cls, *args, **kwargs): 
     obj = super(InheritanceMetaclass, cls).__call__(*args, **kwargs) 
     return obj.get_object() 

class Animal(models.Model): 
    __metaclass__ = InheritanceMetaclass 
    type = models.CharField(max_length=255) 
    object_class = models.CharField(max_length=20) 

    def save(self, *args, **kwargs): 
     if not self.object_class: 
      self.object_class = self._meta.module_name 
     super(Animal, self).save(*args, **kwargs) 
    def get_object(self): 
     if not self.object_class or self._meta.module_name == self.object_class: 
      return self 
     else: 
      return getattr(self, self.object_class) 

class Dog(Animal): 
    def make_sound(self): 
     print "Woof!" 


class Cat(Animal): 
    def make_sound(self): 
     print "Meow!" 

và kết quả mong muốn:

shell$ ./manage.py shell_plus 
From 'models' autoload: Animal, Dog, Cat 
Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41) 
[GCC 4.4.3] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
(InteractiveConsole) 
>>> dog1=Dog(type="Ozzie").save() 
>>> cat1=Cat(type="Kitty").save() 
>>> dog2=Dog(type="Dozzie").save() 
>>> cat2=Cat(type="Kinnie").save() 
>>> Animal.objects.all() 
[<Dog: Dog object>, <Cat: Cat object>, <Dog: Dog object>, <Cat: Cat object>] 
>>> for a in Animal.objects.all(): 
... print a.type, a.make_sound() 
... 
Ozzie Woof! 
None 
Kitty Meow! 
None 
Dozzie Woof! 
None 
Kinnie Meow! 
None 
>>> 

Làm thế nào nó hoạt động?

  1. Lưu trữ thông tin về lớp tên của con vật - chúng tôi sử dụng object_class cho rằng
  2. Remove "proxy" thuộc tính meta - chúng ta cần phải mối quan hệ ngược trong Django (xấu phụ của điều này chúng ta tạo thêm DB bảng cho mỗi mô hình trẻ em và thải thêm DB hit cho rằng, những mặt tốt chúng ta có thể thêm một số con mô hình lĩnh vực phụ thuộc)
  3. Customize save() cho động vật để lưu các lớpTên 210 trong object_class của đối tượng gọi lệnh lưu.
  4. Phương thức get_object là cần thiết để tham chiếu thông qua quan hệ đảo ngược trong Django cho Mô hình có tên được lưu trong bộ nhớ cache trong object_class.
  5. Thực hiện việc này .get_object() "truyền" tự động mỗi lần Động vật khởi tạo bằng cách xác định lại mô hình Metaclass động vật . Metaclass giống như một mẫu cho một lớp (giống như một lớp là một mẫu cho một đối tượng).

Thông tin thêm về metaclass bằng Python: http://www.ibm.com/developerworks/linux/library/l-pymeta.html

0

Câu trả lời này có thể phụ bước các câu hỏi phần nào vì nó không sử dụng mô hình proxy. Tuy nhiên, như các câu hỏi yêu cầu, nó cho phép một viết như sau (và mà không cần phải cập nhật các lớp Animal nếu loại mới được bổ sung) -

animals = Animal.objects.all() 
for animal in animals: 
    animal.make_sound() 

Để tránh lập trình metaclass, người ta có thể sử dụng composition over inheritance. Đối với ví dụ điển

class Animal(models.Model): 

    type = models.CharField(max_length=255) 

    @property 
    def type_instance(self): 
     """Return a Dog or Cat object, etc.""" 
     return globals()[self.type]() 

    def make_sound(self): 
     return self.type_instance.make_sound() 

class Dog(object): 
    def make_sound(self): 
     print "Woof!" 

class Cat(object): 
    def make_sound(self): 
     print "Meow!" 

Nếu DogCat lớp cần truy cập vào Animal Ví dụ, bạn cũng có thể điều chỉnh phương pháp type_instance() ở trên để vượt qua những gì nó cần để các nhà xây dựng lớp học (ví dụ self).

7

Cách tiếp cận metaclass bởi thedk đề nghị thực sự là một cách rất mạnh mẽ để đi, tuy nhiên, tôi đã phải kết hợp nó với một câu trả lời cho câu hỏi here có truy vấn trả về một Proxy mô hình ví dụ. Phiên bản đơn giản của mã được điều chỉnh theo ví dụ trước sẽ là:

from django.db.models.base import ModelBase 

class InheritanceMetaclass(ModelBase): 
    def __call__(cls, *args, **kwargs): 
     obj = super(InheritanceMetaclass, cls).__call__(*args, **kwargs) 
     return obj.get_object() 

class Animal(models.Model): 
    __metaclass__ = InheritanceMetaclass 
    type = models.CharField(max_length=255) 
    object_class = models.CharField(max_length=20) 

    def save(self, *args, **kwargs): 
     if not self.object_class: 
      self.object_class = self._meta.module_name 
     super(Animal, self).save(*args, **kwargs) 

    def get_object(self): 
     if self.object_class in SUBCLASSES_OF_ANIMAL: 
      self.__class__ = SUBCLASSES_OF_ANIMAL[self.object_class] 
     return self 

class Dog(Animal): 
    class Meta: 
     proxy = True 
    def make_sound(self): 
     print "Woof!" 


class Cat(Animal): 
    class Meta: 
     proxy = True 
    def make_sound(self): 
     print "Meow!" 


SUBCLASSES_OF_ANIMAL = dict([(cls.__name__, cls) for cls in ANIMAL.__subclasses__()]) 

Ưu điểm của phương pháp proxy này là không yêu cầu di chuyển db khi tạo các lớp con mới. Nhược điểm là không có trường cụ thể nào có thể được thêm vào các lớp con.

Tôi rất sẵn lòng nhận được phản hồi về phương pháp này.

+0

Công việc tuyệt vời, tự hỏi nếu điều này đứng lên chống lại thử nghiệm thực tế đời sống. – eugene

+1

Tôi nghĩ sẽ thuận tiện khi di chuyển đăng ký SUBCLASSES_OF_ANIMAL tới metaclass '__init__' – eugene

Các vấn đề liên quan