10

Đưa ra mô hình với trường ForeignKeyField (FKF) hoặc ManyToManyField (MTMF) với khóa ngoại để 'tự' làm cách nào để ngăn chặn tự (đệ quy) lựa chọn trong Quản trị Django (quản trị).Cách ngăn chặn tự chọn (đệ quy) cho các trường FK/MTM trong Quản trị Django

Tóm lại, có thể ngăn chặn tự chọn (đệ quy) một phiên bản mẫu trong quản trị viên. Điều này áp dụng khi chỉnh sửa các phiên bản hiện tại của một mô hình, chứ không phải tạo các phiên bản mới.

Ví dụ: lấy mô hình sau đây cho một bài viết trong ứng dụng tin tức;

class Article(models.Model):   
    title = models.CharField(max_length=100) 
    slug = models.SlugField() 
    related_articles = models.ManyToManyField('self') 

Nếu có 3 Article trường (tiêu đề: a1-3), khi chỉnh sửa một Article dụ hiện thông qua quản trị lĩnh vực related_articles được đại diện bởi mặc định bằng một html (nhiều) chọn hộp mà cung cấp một danh sách các TẤT CẢ các bài viết (Article.objects.all()). Người dùng chỉ nên xem và có thể chọn Article trường hợp khác với chính nó, ví dụ: Khi chỉnh sửa Article a1, related_articles có sẵn để chọn = a2, a3.

Tôi hiện có thể thấy 3 tiềm năng để thực hiện việc này, theo thứ tự ưu tiên giảm dần;

  1. Cung cấp một cách để thiết lập các queryset cung cấp lựa chọn có sẵn trong lĩnh vực hình thức quản trị cho các related_articles (thông qua một bộ lọc truy vấn loại trừ, ví dụ như Article.objects.filter(~Q(id__iexact=self.id)) để loại trừ các trường hợp hiện đang được chỉnh sửa từ danh sách các related_articles người dùng có thể xem và Việc tạo/thiết lập của queryset để sử dụng có thể xảy ra bên trong hàm tạo (__init__) của một tùy chỉnh Article ModelForm, hoặc, thông qua một số tùy chọn động limit_choices_to Model. Điều này đòi hỏi một cách để lấy cá thể đang được chỉnh sửa để sử dụng để lọc.
  2. Ghi đè hàm save_model của các lớp Article Model hoặc ModelAdmin để kiểm tra và r tự lật từ related_articles trước khi lưu bản sao. Điều này vẫn có nghĩa là người dùng quản trị có thể xem và chọn tất cả các bài viết kể cả thể hiện đang được chỉnh sửa (đối với các bài viết hiện có).
  3. Lọc ra tham chiếu tự khi được yêu cầu để sử dụng bên ngoài quản trị viên, ví dụ: mẫu.

Giải pháp lý tưởng (1) hiện có thể thực hiện thông qua biểu mẫu tùy chỉnh bên ngoài quản trị vì có thể truyền biến truy vấn đã lọc cho trường hợp đang được chỉnh sửa sang hàm tạo mẫu. Câu hỏi là, bạn có thể nhận được tại cá thể Article, tức là 'tự' đang được chỉnh sửa quản trị viên trước khi biểu mẫu được tạo để làm điều tương tự. Nó có thể là tôi đang đi về điều này một cách sai lầm, nhưng nếu bạn cho phép để xác định một FKF/MTMF cho cùng một mô hình thì có một cách để có quyền admin - làm điều đúng - và ngăn chặn người dùng tự chọn bằng cách loại trừ nó trong danh sách các lựa chọn có sẵn.

Lưu ý: Giải pháp 2 và 3 là có thể làm bây giờ và được cung cấp để thử và tránh nhận được những câu trả lời như, lý tưởng tôi muốn nhận được một câu trả lời cho giải pháp 1.

Trả lời

2

Bạn có thể sử dụng một tùy chỉnh ModelForm trong quản trị viên (bằng cách thiết lập the "form" attribute of your ModelAdmin subclass). Vì vậy, bạn làm điều đó theo cùng một cách trong admin như bạn sẽ bất cứ nơi nào khác.

+0

* Về mặt kỹ thuật *, tôi biết tại sao [limit_choices_to] (https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey .limit_choices_to) không thể làm điều này. Thực tế, tôi không hiểu tại sao nó chưa bao giờ được thực hiện. – kojiro

+2

@kojiro - những điều đơn giản nên đơn giản, những điều nâng cao nên có thể. Không phải mọi tính năng có thể phải được ép vào limit_choices_to, chính vì bạn luôn có thể viết mã ModelForm của riêng bạn để làm bất cứ điều gì bạn muốn. Nếu bạn có một gợi ý cho cú pháp trực quan để thêm điều này vào limit_choices_to mà không làm cho nó phức tạp hơn, bạn được chào đón để mở một vé với bản vá và đề xuất nó. –

9

Carl là đúng, đây là một mẫu cắt và dán mã mà sẽ đi trong admin.py

Tôi tìm thấy điều hướng các mối quan hệ Django có thể được khôn lanh, nếu bạn không có một nền tảng vững chắc, và một tấm gương sống có thể có giá trị 1000 thời gian nhiều hơn một "đi đọc này" (không phải là bạn không cần phải hiểu những gì đang xảy ra).

class MyForm(forms.ModelForm): 
    class Meta: 
     model = MyModel 

    def __init__(self, *args, **kwargs): 
     super(MyForm, self).__init__(*args, **kwargs) 
     self.fields['myManyToManyField'].queryset = MyModel.objects.exclude(
      id__exact=self.instance.id) 
0

Bạn cũng có thể ghi đè lên các phương pháp get_form của ModelAdmin như vậy:

def get_form(self, request, obj=None, **kwargs): 
    """ 
    Modify the fields in the form that are self-referential by 
    removing self instance from queryset 
    """ 
    form = super().get_form(request, obj=None, **kwargs) 
    # obj won't exist yet for create page 
    if obj: 
     # Finds fieldnames of related fields whose model is self 
     rmself_fields = [f.name for f in self.model._meta.get_fields() if (
      f.concrete and f.is_relation and f.related_model is self.model)] 
     for fieldname in rmself_fields: 
      form.base_fields[fieldname]._queryset = 
       form.base_fields[fieldname]._queryset.exclude(id=obj.id) 
    return form 

Lưu ý rằng đây là một on-size-fits-tất cả các giải pháp mà tự động tìm kiếm các lĩnh vực mô hình tự tham khảo và loại bỏ tự từ tất cả chúng :-)