2010-03-30 26 views
7

Giống như trong this question, ngoại trừ tôi muốn để có thể có queryset mà trả về một cơ thể hỗn hợp của các đối tượng:mô hình django subclassed với queryset tích hợp

>>> Product.objects.all() 
[<SimpleProduct: ...>, <OtherProduct: ...>, <BlueProduct: ...>, ...] 

tôi đã tìm ra rằng tôi không thể chỉ cần đặt Product.Meta.abstract true hoặc bằng cách khác HOẶC cùng với các querys của các đối tượng khác nhau. Tốt thôi, nhưng đây là tất cả các lớp con của một lớp phổ biến, vì vậy nếu tôi rời khỏi lớp bậc trên của chúng là không trừu tượng, tôi sẽ rất vui, miễn là tôi có thể đưa trình quản lý của nó trả về các đối tượng của lớp thích hợp. Mã truy vấn trong django làm điều của nó, và chỉ thực hiện cuộc gọi đến Product(). Nghe có vẻ dễ dàng đủ, ngoại trừ nó thổi lên khi tôi ghi đè Product.__new__, tôi đoán vì __metaclass__ trong Model ... Đây là mã phi django mà ứng xử khá nhiều làm thế nào tôi muốn nó:

class Top(object): 
    _counter = 0 
    def __init__(self, arg): 
     Top._counter += 1 
     print "Top#__init__(%s) called %d times" % (arg, Top._counter) 
class A(Top): 
    def __new__(cls, *args, **kwargs): 
     if cls is A and len(args) > 0: 
      if args[0] is B.fav: 
       return B(*args, **kwargs) 
      elif args[0] is C.fav: 
       return C(*args, **kwargs) 
      else: 
       print "PRETENDING TO BE ABSTRACT" 
       return None # or raise? 
     else: 
      return super(A).__new__(cls, *args, **kwargs) 
class B(A): 
    fav = 1 
class C(A): 
    fav = 2 
A(0) # => None 
A(1) # => <B object> 
A(2) # => <C object> 

Nhưng điều đó không nếu tôi kế thừa từ django.db.models.Model thay vì object:

File "/home/martin/beehive/apps/hello_world/models.py", line 50, in <module> 
    A(0) 
TypeError: unbound method __new__() must be called with A instance as first argument (got ModelBase instance instead) 

Đó là một vết lùi đáng chú ý là không hấp dẫn; Tôi cũng không thể bước vào khung mã số __new__ của mình trong trình gỡ lỗi. Tôi đã thử một cách khác nhau super(A, cls), Top, super(A, A) và tất cả những điều trên kết hợp với việc chuyển số cls vào làm đối số đầu tiên cho __new__, tất cả đều không có kết quả. Tại sao điều này đá tôi quá khó? Tôi có phải tìm ra metaclasses của django để có thể sửa lỗi này hoặc có cách nào tốt hơn để hoàn thành kết thúc của tôi không?

+1

Thật là hấp dẫn để tìm ra câu đố, nhưng bản năng nói với tôi rằng bạn đang làm điều đó sai. Điều này giống như tra tấn đến ORM nghèo nàn của Django. – keturn

Trả lời

4

Về cơ bản những gì bạn đang cố gắng làm là trả lại các lớp con khác nhau, trong khi truy vấn một lớp cơ sở được chia sẻ. Đó là: bạn muốn các lớp lá.Kiểm tra đoạn mã này để biết giải pháp: http://www.djangosnippets.org/snippets/1034/

Ngoài ra, hãy chắc chắn kiểm tra các tài liệu về khung nội dung của Django: http://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/ Ban đầu có thể hơi khó hiểu, nhưng Contenttypes sẽ giải quyết các vấn đề khác mà bạn có thể gặp phải khi sử dụng không các lớp cơ sở trừu tượng với ORM của Django.

+0

được bỏ phiếu để không sử dụng '__new__' hoặc' __metaclass__'. – keturn

+0

liên kết đoạn mã bị hỏng. – freyley

1

Được rồi, công trình này: https://gist.github.com/348872

Các chút khéo léo là đây.

class A(Top): 
    pass 

def newA(cls, *args, **kwargs): 
    # [all that code you wrote for A.__new__] 

A.__new__ = staticmethod(newA) 

Bây giờ, có điều gì đó về cách Python liên kết __new__ mà tôi có thể không hoàn toàn hiểu, nhưng các ý chính của nó là thế này: ModelBase metaclass django của tạo ra một đối tượng lớp mới, thay vì sử dụng một trong đó là thông qua năm đến số __new__; gọi số A_prime. Sau đó, nó dính tất cả các thuộc tính mà bạn có trong định nghĩa lớp cho A vào A_prime, nhưng __new__ không bị ràng buộc lại chính xác.

Sau đó, khi bạn đánh giá A(1), A thực sự là A_prime ở đây, gọi python <A.__new__>(A_prime, 1), không khớp và phát nổ.

Vì vậy, giải pháp là xác định __new__sau khiA_prime của bạn đã được xác định.

Có thể đây là lỗi trong django.db.models.base.ModelBase.add_to_class, có thể đó là lỗi trong Python, tôi không biết.

Bây giờ, khi tôi nói "công trình này" trước đó, tôi có nghĩa là hoạt động riêng biệt với trường hợp kiểm tra xây dựng đối tượng tối thiểu trong phiên bản SVN hiện tại của Django. Tôi không biết nếu nó thực sự hoạt động như một mô hình hoặc là hữu ích trong một QuerySet. Nếu bạn thực sự sử dụng điều này trong mã sản xuất, tôi sẽ thực hiện một cuộc nói chuyện công khai từ nó cho pdxpython và họ sẽ chế nhạo bạn cho đến khi bạn mua cho chúng tôi tất cả pizza không chứa gluten.

+0

Heh. Vâng, điều đó khiến tôi trở thành hành vi phân lớp cần thiết, nhưng nó có thể phá vỡ mọi mối quan hệ của tôi! Không có đối tượng nào của tôi sau đó có thể liên quan đến các mô hình khác. Tôi sẽ nghiên cứu thêm để xem liệu tôi có thể khắc phục khía cạnh đó không. – outofculture

1

Chỉ cần dán @staticmethod trước phương thức __new__.

@staticmethod 
def __new__(cls, *args, **kwargs): 
    print args, kwargs 
    return super(License, cls).__new__(cls, *args, **kwargs)