2017-06-29 14 views
7

Bất kỳ ai biết cách tạo ra một lĩnh vực ngoại giao và làm cho nó luôn luôn trỏ đến cùng một mô hình, cho đến nay tôi nhận được những điều này.Custom django foreignfield

class PanMachineTimeUnitField(models.ForeignKey): 
    def __init__(self, **kwargs): 
     to = 'panbas.PanBasTimeUnit' 
     kwargs['verbose_name'] = _('Machine Unit') 
     kwargs['related_name'] = 'machine_unit' 
     super(PanMachineTimeUnitField, self).__init__(to, **kwargs) 

Nhưng tôi gặp lỗi khi bắt đầu. Tôi muốn sử dụng nó như,

machine_unit = PanMachineTimeUnitField() 

Không cần khai báo thêm.

Chỉnh sửa: Tôi muốn điều này bởi vì, tôi sẽ có ngoại tuyến này ở một số nơi yên tĩnh. Nếu tôi muốn thay đổi verbose_name của trường, tôi muốn tất cả các trường của tôi bị ảnh hưởng bởi thay đổi này. Tên chi tiết là một ví dụ, nó có thể là một thuộc tính khác.

Tôi không muốn sử dụng cài đặt py để khai báo giá trị mặc định.

+0

đăng lỗi của bạn có thể? – Todor

Trả lời

8

tôi khuyên bạn nên sử dụng chỉ một chức năng đơn giản để tạo ra một trường hợp tương tự cấu hình sẵn của ForeignKey: (không phải là một thể hiện của lớp con của ForeignKey)

def pan_machine_time_unit_field(**kwargs): 
    othermodel = 'panbas.PanBasTimeUnit' 
    on_delete = models.DO_NOTHING # or what you need 
    kwargs['verbose_name'] = 'Machine Unit' 
    kwargs.setdefault('related_name', '+') 
    # or: kwargs.setdefault('related_name', "%(app_label)s_%(class)s_related", 
    return models.ForeignKey(othermodel, on_delete, **kwargs) 

class C(models.Model): 
    machine_unit = pan_machine_time_unit_field() 
    # or: 
    # machine_unit = pan_machine_time_unit_field(related_name='klass_c_children') 

Thuộc tính related_name là một tên được sử dụng cho quan hệ lạc hậu từ đối tượng đích của othermodel đến tất cả các đối tượng tham chiếu đến nó. Tên đó phải là duy nhất trên othermodel ('panbas.PanBasTimeUnit', thường là một cái gì đó với tên ứng dụng và lớp đó là duy nhất đủ) hoặc tên đó có thể là '+' nếu bạn không muốn tạo tập hợp truy vấn quan hệ lạc hậu. Cả hai biến thể được ngụ ý trong ví dụ. Đồng thời nhớ on_delete.

Nếu bạn thực sự cần phải tạo một lớp con (có ý nghĩa nếu cần thêm nhiều phương thức), bạn cũng phải xác định phương thức deconstruct để di chuyển. Sẽ rất phức tạp nếu bạn cần sửa đổi phân lớp đó sau. Nó có thể không bao giờ bị xóa, đổi tên vv do migrations on a custom field. Mặt khác, nếu bạn tạo một cá thể đơn giản của ForeignKey trực tiếp bởi một hàm, tất cả về việc di chuyển có thể bị bỏ qua.


EDIT

Ngoài ra bạn có thể tạo một abstract base model với lĩnh vực đó và tạo ra mô hình mới bằng cách thừa kế hoặc multiple inheritance:

class WithPanBasTimeUnit(models.Model): 
    machine_unit = models.ForeignKey(
     'panbas.PanBasTimeUnit', 
     models.DO_NOTHING, 
     verbose_name=_('Machine Unit'), 
     related_name='%(app_label)s_%(class)s_related' 
    ) 

    class Meta: 
     abstract = True 

class ExampleModel(WithPanBasTimeUnit, ...or more possible base models...): 
    ... other fields 

Giải pháp này (lấy cảm hứng từ một SOUTION không hợp lệ Ykh) hữu ích nếu bạn muốn thêm phương thức vào các mô hình với trường đó hoặc để thêm các trường khác cùng nhau, nếu không thì giải pháp gốc là easie r.

6
class PanBasTimeUnit(models.Model): 
    machine_unit = models.ForeignKey('self', blank=True, null=True, 
           verbose_name=u'parent') 

sử dụng 'self' hoặc 'panbas.PanBasTimeUnit' sẽ ổn.

+1

Tôi không muốn cung cấp bất kỳ tham số nào cho khóa ngoại "Không cần khai báo thêm." – durdenk

+0

Điều này sẽ thêm trường tham chiếu chéo không mong muốn từ một đối tượng PanBasTimeUnit vào đối tượng PanBasTimeUnit khác và nó không giải quyết bất kỳ điều gì hữu ích cho câu hỏi. Tôi đang sửa giải pháp của bạn và thêm nó vào cuối câu trả lời của tôi, bởi vì nó quá dài vào một bình luận. – hynekcer

1

lý do tại sao không sử dụng trực tiếp machine_unit = models.ForeignKey(panbas.PanBasTimeUnit, verbose_name=_('Machine Unit'), related_name='machine_unit')) ?

+0

Tôi sẽ có ngoại ngữ này ở những nơi yên tĩnh, vì vậy tôi muốn nó được quản lý ở một nơi mà nếu tôi thay đổi tên tiết, tôi muốn tất cả các tên nước ngoài verbose_name được thay đổi. – durdenk

+0

những gì về việc thiết lập tên tiết trong settings.py sau đó: MY_VERBOSE_NAME = _ ('Đơn vị máy'), và trong mô hình: machine_unit = models.ForeignKey (panbas.PanBasTimeUnit, verbose_name = settings.MY_VERBOSE_NAME, related_name = 'machine_unit')) – Yom86

3

Một sửa đổi nhỏ trong nỗ lực của bạn sẽ làm công việc của bạn.

class PanMachineTimeUnitField(models.ForeignKey): 
    def __init__(self, **kwargs): 
     kwargs["to"] = 'panbas.PanBasTimeUnit' 
     kwargs['verbose_name'] = _('Machine Unit') 
     kwargs['related_name'] = 'machine_unit' 
     super(PanMachineTimeUnitField, self).__init__(**kwargs) 
4

Bạn không thể có nhiều Khóa ngoại cho một kiểu có cùng số related_name.

Thật vậy, trên phiên bản PanBasTimeUnit, người quản lý nào nên Django trở lại khi gọi <instance>.machine_unit? Đây là lý do tại sao bạn phải cẩn thận trên related models and abstract classes.

Nó sẽ hoạt động tốt nếu bạn xóa kwargs['related_name'] = 'machine_unit' trong mã của mình và thay thế bằng kwargs['related_name'] = "%(app_label)s_%(class)s_related" hoặc tương tự.