2008-12-08 31 views
11

Tôi đang cố gắng tìm lớp thực tế của đối tượng mô hình django, khi sử dụng mô hình thừa kế.Làm cách nào để tìm "lớp bê tông" của mô hình django baseclass

Một số mã để mô tả các vấn đề:

class Base(models.model): 
    def basemethod(self): 
     ... 

class Child_1(Base): 
    pass 

class Child_2(Base): 
    pass 

Nếu tôi tạo các đối tượng khác nhau của hai lớp trẻ và tạo ra một queryset chứa tất cả:

Child_1().save() 
Child_2().save() 
(o1, o2) = Base.objects.all() 

Tôi muốn để xác định xem đối tượng thuộc loại Child_1 hoặc Child_2 trong basemethod, tôi có thể tới đối tượng con thông qua o1.child_1 và o2.child_2 nhưng nó sẽ kiểm tra kiến ​​thức về các lớp con trong baseclass.

tôi đã đưa ra đoạn mã sau:

def concrete_instance(self): 
    instance = None 
    for subclass in self._meta.get_all_related_objects(): 
     acc_name = subclass.get_accessor_name() 
     try: 
      instance = self.__getattribute__(acc_name) 
      return instance 
     except Exception, e: 
      pass 

Nhưng nó cảm thấy giòn và tôi không chắc chắn về những gì sẽ xảy ra khi nếu tôi thừa hưởng ở mức độ nhiều hơn.

Trả lời

13

Django thực hiện kế thừa mô hình với OneToOneField giữa bảng của mô hình gốc và bảng của mô hình con. Khi bạn làm Base.object.all(), Django chỉ truy vấn bảng Cơ sở và do đó không có cách nào để biết bảng con là gì. Do đó, rất tiếc, không thể truy cập trực tiếp vào cá thể mô hình con mà không có truy vấn bổ sung.

snippet này cho thấy một phương pháp phổ biến của việc thêm một lĩnh vực ContentType với mô hình cơ sở:

from django.contrib.contenttypes.models import ContentType 

class Base(models.Model): 
    content_type = models.ForeignKey(ContentType,editable=False,null=True) 

    def save(self): 
     if(not self.content_type): 
      self.content_type = ContentType.objects.get_for_model(self.__class__) 
     self.save_base() 

    def as_leaf_class(self): 
     content_type = self.content_type 
     model = content_type.model_class() 
     if(model == Base): 
      return self 
     return model.objects.get(id=self.id) 

Sau đó bạn có thể nói if Base.content_type.model_class() để xác định loại.

Here là một đoạn mã khác thêm người quản lý tùy chỉnh vào danh sách kết hợp.

Như bạn có thể thấy, cả hai giải pháp này đều có tiềm năng cực kỳ tốn kém. Nếu bạn có một số lượng lớn các cá thể, sử dụng phương thức as_leaf_class() sẽ yêu cầu một truy vấn trên mỗi mục.Thay vào đó, nếu bạn có một tập hợp các mô hình con đã biết, chỉ cần truy vấn từng mô hình một cách riêng biệt và tổng hợp các phiên bản thành một danh sách.

+0

Cảm ơn, đã giải quyết được vấn đề của tôi –

+0

Tôi không biết nếu đó là vì tôi đang trên django 1.4, nhưng điều này không làm việc cho tôi cho đến khi tôi sử dụng 'if (not self.content_type_id)' thay vì self.content_type. rõ ràng Django đã cố gắng để tải self.content_type, do đó dẫn đến 'DoesNotExist' –

+0

AFAIK này là chính xác những gì gói này không: https://django-polymorphic.readthedocs.org/en/latest/ Nó trả về lớp con cụ thể ngay cả khi bạn sử dụng BaseClass.objects.filter() – guettli

-2

Nó cảm thấy giòn vì nó. (Đây là bản in lại của câu trả lời trong một ngữ cảnh khác. See C++ casting programmatically : can it be done ?)

Đọc trên đa hình. Hầu như mọi tình huống "diễn viên năng động" là một ví dụ về tính đa hình đang phải vật lộn để được thực hiện.

Bất kỳ quyết định nào bạn thực hiện trong dàn diễn viên năng động đã được thực hiện. Chỉ cần ủy nhiệm công việc thực sự cho các lớp con.

Bạn đã để lại phần quan trọng nhất trong ví dụ của mình. Công việc hữu ích, đa hình.

Khi bạn nói "Tôi muốn xác định xem đối tượng có thuộc loại Child_1 hoặc Child_2 ..." bạn đã bỏ qua "để tôi có thể tạo đối tượng làm aMethod() theo cách duy nhất cho mỗi phân lớp". Phương thức đó là công việc hữu ích và nó chỉ đơn giản là phương thức của cả hai lớp con.

class Base(models.model): 
    def aMethod(self): 
     # base class implementation. 

class Child_1(Base): 
    def aMethod(self): 
     # Child_1 override of base class behavior. 

class Child_2(Base): 
    def aMethod(self): 
     supert(Child_2, self).aMethod() # Invoke the base class version 
     # Child_2 extension to base class behavior. 

Phương pháp tương tự, nhiều triển khai. Không bao giờ cần "xác định loại thời gian chạy" hoặc xác định lớp bê tông.

+0

Tuy nhiên, ông không có một tham chiếu đến dụ một mô hình trẻ em của. Trình quản lý Base.objects chỉ trả về các cá thể của Base. –

+0

@Daniel: Thông thường chúng tôi xử lý Child_1.objects.all() và Child_2.objects.all(); hầu như không có cuộc gọi nào cho sự kết hợp của cả hai kiểu con. –

+0

Nói chung, nhưng đó không phải là câu hỏi. –

0

Vâng ... Vấn đề của tôi là. Theo quan điểm, tôi đã có mô hình chính này, cho phép nói "Big_Model" và có một số "Small_Model" liên quan đến "Big_Model". Vì vậy, khi tôi muốn lấy tất cả "Small_Model" liên quan đến một ví dụ nhất định của "Big_Model" tôi đã làm điều đó ** _ set.all(). Nhưng vấn đề là Small_Model có các lớp con và tôi muốn, trong view.py, để có được lớp con nào là từng trường hợp Small_Model liên quan đến. Bí quyết của tôi là xác định các phương thức boolean trong mô hình Small_Model như is_child_1() và is_child_2(). Và khi nó đúng, bạn áp dụng con trỏ thực tế thay vì con trỏ Small_Model.

Ok ... Thats không đủ rõ ràng, tôi vẫn không có nhiều thời gian để viết một ví dụ điển hình, vì vậy tôi sẽ chỉ cần sao chép-dán trường hợp của tôi ở đây:

class Cache(models.Model): 
    valor = models.DecimalField(max_digits=9, decimal_places=2, blank= True, null= True) 
    evento=models.ForeignKey(Evento) 
    def __unicode__(self): 
    return u'%s: %s' % (self.evento, self.valor) 
    class Meta: 
    verbose_name='Cachê' 
    verbose_name_plural='Cachês' 
    def is_cb(self): 
    try: 
     self.cache_bilheteria 
     return True 
    except self.DoesNotExist: 
     return False 
    def is_co(self): 
    try: 
     self.cache_outro 
     return True 
    except self.DoesNotExist: 
     return False 
5

Có một cái nhìn tại InheritanceManager trong django-model-utils - gắn nó vào một mô hình mang đến cho bạn lớp con cụ thể (ít nhất là ở cấp đầu tiên):

from model_utils.managers import InheritanceManager 

class Base(models.Model): 
    objects = InheritanceManager() 

# ... 

Base.objects.all().select_subclasses() # returns instances of child classes 

mô hình-utils đòi hỏi Django 1.2 hoặc cao hơn.

0

Hơi sửa đổi phiên bản của what Daniel Naab proposed:

from django.contrib.contenttypes.models import ContentType 
from django.db import models 

def ParentClass(models.Model): 
    superclass = models.CharField(max_length = 255, blank = True) 

    def save(self, *args, **kwargs): 
     if not self.superclass: 
      self.superclass = ContentType.objects.get_for_model(self.__class__) 

     super(ParentClass, self).save(*args, **kwargs) 

    def getChild(self): 
     s = getattr(self, self.superclass) 
     if hasattr(s, 'pk'): 
      return s 
     else: 
      return None 

class Child1(ParentClass): 
    pass 

class Child2(ParentClass): 
    pass 
Các vấn đề liên quan