5

Tôi đang tạo trang web django có plugin. Mỗi plugin là một ứng dụng django đơn giản tùy thuộc vào một ứng dụng chính (hoặc thậm chí các plugin khác).Làm thế nào để khỉ vá mô hình xử lý phía nam từ plugin?

Mặc dù phụ thuộc giữa các ứng dụng/plugin rõ ràng đối với tôi, nên thêm cột (như khóa ngoài cho các mô hình cụ thể của plugin) thông qua bản vá khỉ để tránh ứng dụng chính phụ thuộc vào plugin.

Vì ứng dụng chính đã có quản lý ở phía nam và do đó có tất cả các plugin, tôi không thể thay đổi thư mục di chuyển trong cài đặt cho các mô-đun đó.

Vì vậy, làm thế nào để khỉ vá một mô hình ứng dụng phía nam từ một ứng dụng phía nam khác?

ps: Tôi là người Pháp, vui lòng sửa câu hỏi của tôi nếu bạn phát hiện ra bất kỳ lỗi nào hoặc hỏi bất cứ điều gì nếu tôi không rõ ràng.

Chỉnh sửa: Tôi đã thêm câu trả lời về cách tôi làm bây giờ trên di chuyển django.

+2

bảng Thay đổi cấu trúc qua monkeypatch là IMHO một ý tưởng rất rất xấu. Bạn có thể muốn sử dụng GenericForeignKeys hoặc bất kỳ giải pháp "không phá hủy" đặc biệt khác thay thế. –

+0

Đó là hiệu quả hơn, không phải là xấu xí, và tôi là nhà phát triển của tất cả những ứng dụng (vì vậy không có rủi ro của xung đột không giám sát). Ngay cả khi tôi chưa bao giờ sử dụng GenericForeignKeys nhưng tôi không thích chúng. – christophe31

+0

Tôi không hiểu vấn đề. Một bản vá khỉ thích hợp sẽ làm cho nó không đáng kể từ phía nam. Bạn chỉ cần thêm tập lệnh di chuyển đệ quy. – deufeufeu

Trả lời

3

Hiện tại giải pháp tốt nhất của tôi là tạo tệp di chuyển của riêng mình trong trình cắm thêm (ngụ ý thêm bảng trong từ điển mô hình của tệp di chuyển).

Tôi sẽ xem sau về di chuyển tiếp theo nếu tất cả các mô hình sẽ tự động theo dõi.

Trong tập tin chuyển đổi mới của tôi:

class Migration(SchemaMigration): 
    def forwards(self, orm): 
     db.add_column(u'remoteapp_model', 'fieldname', 
         self.gf('django.db.models.fields.related.ForeignKey', 
         (to=orm["my_plugin.MyModel"], default=None, null=True, blank=True), 
         keep_default=False) 


    def backwards(self, orm): 
     db.delete_column(u'remoteapp_model', 'fieldname') 

    # for models, you may want to copy from a previous migration file 
    # and add from the models of the main application the related tables 
    models = {} 

Trong tập mô hình của tôi:

from remoteapp.models import RemoteModel 
from django.db import models 

class MyModel(models.Model): 
    pass 

models.ForeignKey(MyModel, null=True, blank=True, 
        default=None).contribute_to_class(RemoteModel, 'my_model') 
0

Đối với tình yêu của Chúa Kitô không sử dụng monkeypatching cho việc này. Sử dụng thừa kế, đó là những gì nó cho.

Chỉ cần có các plugin cần làm việc với nhiều trường mở rộng mô hình hiện có và sau đó sử dụng một trong nhiều kỹ thuật để có được cá thể lớp chuyên biệt nhất của mô hình. Tôi sử dụng các lớp dưới đây như một mixin trên tất cả các lớp học sẽ được sử dụng theo cách này, để đạt được điều này.

class Specializable(object): 

    @classmethod 
    def all_classes(selfclass): 
     """ Returns the class this is called on, plus its known subclasses """ 
     subs = selfclass.__subclasses__() 
     subs.insert(0, selfclass) 
     return subs 

    def get_sub_instance(self): 
     """ Gets concrete instance of object of deepest subtype which has its ancestor link pointing to this object (depth first search behaviour). """ 
     selftype = type(self) 
     for klass in selftype.__subclasses__(): 
      this_ptr_name = klass._meta.get_ancestor_link(selftype).name 
      try: 
       sub = klass.objects.get(**{this_ptr_name: self}) 
       subsub = sub.get_sub_instance() 
       if subsub: return subsub 
       else: return sub 
      except ObjectDoesNotExist: 
       pass 

    @classmethod 
    def new_with_translator(selfclazz, name): 
     def new(cls, *args, **kwargs): 
      selfclazz.create_subclass_translator(cls, install = name) 
      return models.Model.__new__(cls, *args, **kwargs) 

     return new 

    @classmethod 
    def create_subclass_translator(selfclazz, Baseclass, install=None): 
     """ Creates a classmethod object for installation on Baseclass, 
     which acts as a factory for instances of subclasses of Baseclass, 
     when called on that subclass. The factory takes as input an instance 
     of a subclass (old_instance), and a dictionary of properties with which 
     to initialise the new instance. It also installs the base class instance 
     of old_instance as the base instance for the new instance. All three will 
     share the same pk. 

     if install is set, this will also install the function on Baseclass under 
     that name if it does not have a property of that name. """ 

     def create_from_other_instance(selfclass, old_instance, properties): 
      """ Creates an instance of this class using properties and old_instance. 
      In particular, it will try to re-use the superclass instance of old_instance. 
      properties should contain all of the fields for this class (but need not include the superclass values) 
      This *must* be called on a subclass of the baseclass - it will give odd results if called on the baseclass itself. 
      This will NOT delete old_instance and it will NOT SAVE the object created - the caller is responsible for 
      those things.""" 

      if selfclass is Baseclass: raise TypeError("This method cannot be used on the base class") 

      ancestor_link = selfclass._meta.get_ancestor_link(Baseclass).name 
      properties.update({ancestor_link: getattr(old_instance,ancestor_link)}) 
      for f in get_model_fields(Baseclass): 
       val = getattr(old_instance, f) 
       if val and not properties.get(f): 
        properties[f] = val 

      return selfclass(**properties) 

     new_method = classmethod(create_from_other_instance) 

     if install and not hasattr(Baseclass, install): 
      setattr(Baseclass, install, new_method) 

     return new_method 

Dưới đây là một ví dụ về việc sử dụng nó, từ một số mã của tôi:

#KYCable inherits from Model, so must precede Model 
class Director(KYCable, models.Model, Specializable, DateFormatter, AdminURL, Supercedable): 
    def get_individual(self): 
     return self.get_sub_instance().get_individual() 

Như bạn thấy, Specializable lớp cần phải lưu ý rằng phương pháp của họ có thể bị ghi đè bởi lớp con, và được mã hóa thích hợp.

Nếu plugin của bạn biết về mối quan hệ lớp con, thì nó có thể sử dụng các thủ thuật như tìm kiếm trên cùng một id mô hình trong các lớp con mà nó biết, để có được cá thể mô hình phân lớp tương ứng khi được trình bày với một cá thể siêu lớp.

+0

Tạo bảng mới, cần tham gia, giảm hiệu suất, ít thực tế hơn, yêu cầu nhiều mã hơn ... Bạn không thuyết phục tôi về "vá khỉ là xấu xí", bạn không đưa ra lý lẽ. Nhưng bạn có tiền thưởng vì bạn là người duy nhất trả lời. Bạn đọc câu hỏi và trả lời câu hỏi bạn muốn trả lời, không phải câu hỏi tôi đã trả lời. Nhưng cảm ơn câu trả lời, được xây dựng tốt. – christophe31

+0

@ christophe31 Đó là thực tế hơn, bởi vì bạn đang sử dụng một công cụ như nó được cho là được sử dụng, và vì vậy tránh giới thiệu các lỗi khó tìm, cũng như tránh sự cần thiết phải viết tay di chuyển của riêng bạn. Không có onus trên tôi để cho bạn biết làm thế nào để làm điều gì đó khủng khiếp. Monkeypatching là cho một hệ thống đối tượng năng động. Khi bạn ánh xạ nó tới một cơ sở dữ liệu quan hệ, bạn có một hệ thống lớp tĩnh được đại diện trong bộ nhớ bởi một hệ thống đối tượng động. Ngoài ra, bạn có xem xét cách bạn sẽ đối phó với hai plugin bổ sung thêm tên trường giống nhau không? Thừa kế một lần nữa tránh được vấn đề đó. – Marcin

+0

@ christophe31 Theo quan điểm của bạn về hiệu suất - tối ưu hóa sớm là gốc rễ của mọi điều ác. – Marcin

1

Tôi đăng câu trả lời mới khi di chuyển sang django 1.7 và di chuyển django, giải pháp không rõ ràng, tôi phải tạo lớp di chuyển của riêng mình để thêm khóa ngoài vào bảng từ xa.

from django.db.migrations import AddField 

class AddRemoteField(AddField): 
    def __init__(self, remote_app, *args, **kwargs): 
     super(AddRemoteField, self).__init__(*args, **kwargs) 
     self.remote_app = remote_app 

    def state_forwards(self, app_label, *args, **kwargs): 
     super(AddRemoteField, self).state_forwards(self.remote_app, *args, **kwargs) 

    def database_forwards(self, app_label, *args, **kwargs): 
     super(AddRemoteField, self).database_forwards(
      self.remote_app, *args, **kwargs) 

    def database_backwards(self, app_label, *args, **kwargs): 
     super(AddRemoteField, self).database_backwards(
      self.remote_app, *args, **kwargs) 

Và sau đó tôi thực hiện một tập tin chuyển đổi:

from __future__ import unicode_literals 

from django.db import models, migrations 
from my_app.tools import AddRemoteField 
from my_app.models import Client 


class Migration(migrations.Migration): 

    dependencies = [ 
     ('anikit', '0002_manual_user_migration'), 
    ] 

    operations = [ 
     AddRemoteField(
      remote_app='auth', 
      model_name='user', 
      name='client', 
      field=models.ForeignKey(Client, verbose_name='client', 
            null=True, blank=True), 
      preserve_default=True, 
     ), 
    ] 
Các vấn đề liên quan