2009-07-30 36 views
22

Tôi có thiết lập quản trị sau để tôi có thể thêm/chỉnh sửa người dùng và tiểu sử của họ cùng một lúc.Làm cách nào để yêu cầu nội tuyến trong Quản trị viên Django?

class ProfileInline(admin.StackedInline): 
    """ 
    Allows profile to be added when creating user 
    """ 
    model = Profile 


class UserProfileAdmin(admin.ModelAdmin): 
    """ 
    Options for the admin interface 
    """ 
    inlines = [ProfileInline] 
    list_display = ['edit_obj', 'name', 'username', 'email', 'is_active', 
     'last_login', 'delete_obj'] 
    list_display_links = ['username'] 
    list_filter = ['is_active'] 
    fieldsets = (
     (None, { 
      'fields': ('first_name', 'last_name', 'email', 'username', 
       'is_active', 'is_superuser')}), 
     ) 
    ordering = ['last_name', 'first_name'] 
    search_fields = ['first_name', 'last_name'] 

admin.site.register(User, UserProfileAdmin) 

Vấn đề là tôi cần hai trường trong biểu mẫu nội tuyến Tiểu sử cần thiết khi thêm người dùng. Biểu mẫu nội dòng không xác thực trừ khi nhập vào. Có anyway để làm cho nội tuyến cần thiết, để nó không thể để trống?

Trả lời

29

Tôi đã lấy lời khuyên của Carl và thực hiện tốt hơn nhiều sau đó là hack-ish tôi đã đề cập trong bình luận của tôi để trả lời của mình. Đây là giải pháp của tôi:

Từ forms.py tôi:

from django.forms.models import BaseInlineFormSet 


class RequiredInlineFormSet(BaseInlineFormSet): 
    """ 
    Generates an inline formset that is required 
    """ 

    def _construct_form(self, i, **kwargs): 
     """ 
     Override the method to change the form attribute empty_permitted 
     """ 
     form = super(RequiredInlineFormSet, self)._construct_form(i, **kwargs) 
     form.empty_permitted = False 
     return form 

Và admin.py

class ProfileInline(admin.StackedInline): 
    """ 
    Allows profile to be added when creating user 
    """ 
    model = Profile 
    extra = 1 
    max_num = 1 
    formset = RequiredInlineFormSet 


class UserProfileAdmin(admin.ModelAdmin): 
    """ 
    Options for the admin interface 
    """ 
    inlines = [ProfileInline] 
    list_display = ['edit_obj', 'name', 'username', 'email', 'is_active', 
     'last_login', 'delete_obj'] 
    list_display_links = ['username'] 
    list_filter = ['is_active'] 
    fieldsets = (
     (None, { 
      'fields': ('first_name', 'last_name', 'email', 'username', 
       'is_active', 'is_superuser')}), 
     (('Groups'), {'fields': ('groups',)}), 
    ) 
    ordering = ['last_name', 'first_name'] 
    search_fields = ['first_name', 'last_name'] 


admin.site.register(User, UserProfileAdmin) 

này thực hiện chính xác những gì tôi muốn, nó làm cho validate Hồ sơ inline formset. Vì vậy, vì có các trường bắt buộc trong biểu mẫu hồ sơ, nó sẽ xác thực và thất bại nếu thông tin bắt buộc không được nhập vào biểu mẫu nội tuyến.

+1

Nếu bạn đang sử dụng 'GenericInlineModelAdmin', thay thế' BaseInlineFormSet' bằng 'BaseGenericInlineFormSet'. – L42y

+1

Cảm ơn! Tuy nhiên, nếu biểu mẫu của bạn không có các trường bắt buộc, biểu mẫu sẽ vẫn không xác thực và lưu khi trống. Khỉ-vá các hình thức bằng cách sử dụng 'form.has_changed = lambda: True' để có nó được lưu mặc dù bị trống. – bouke

9

Bạn có thể làm điều này, nhưng bạn sẽ phải làm cho đôi tay của bạn bị bẩn trong mã formset/inline.

Trước hết, tôi nghĩ bạn muốn luôn có một biểu mẫu trong tập hợp mẫu trong trường hợp này và không bao giờ nhiều hơn một, do đó bạn sẽ muốn đặt max_num = 1 và extra = 1 trong Tiểu sử của mình.

Vấn đề cốt lõi của bạn là BaseFormSet._construct_form passes empty_permitted=True cho mỗi biểu mẫu "bổ sung" (ví dụ: trống) trong bộ định dạng. Tham số này yêu cầu biểu mẫu bỏ qua xác thực nếu nó không thay đổi. Bạn chỉ cần tìm một cách để đặt empty_permitted = False cho biểu mẫu.

Bạn có thể use your own BaseInlineFormset subclass trong nội tuyến của mình để có thể trợ giúp. Nhận thấy rằng _construct_form mất ** kwargs và cho phép để ghi đè các kwarg được truyền cho các cá thể Form riêng biệt, bạn có thể ghi đè lên _construct_forms trong lớp con Formset của bạn và nó vượt qua empty_permitted = False trong mọi lời gọi đến _construct_form. Nhược điểm là bạn đang dựa vào các API nội bộ (và bạn phải viết lại _construct_forms).

Ngoài ra, bạn có thể thử cách ghi đè các phương pháp get_formset trên ProfileInline của bạn, và sau khi gọi get_formset của cha mẹ, tay chọc vào hình thức bên trong formset trả về:

def get_formset(self, request, obj=None, **kwargs): 
    formset = super(ProfileInline, self).get_formset(request, obj, **kwargs) 
    formset.forms[0].empty_permitted = False 
    return formset 

Chơi xung quanh và xem những gì bạn có thể làm cho công việc !

+0

Cảm ơn thông tin.Tôi đã đưa ra một giải pháp nhưng nó là rất hack-ish, và tôi không đặc biệt tự hào về nó. Tôi đã kết thúc việc ghi đè lên add_view cho ModelAdmin và sao chép tất cả mã từ chế độ xem mặc định và sửa đổi các giá trị bộ định dạng. Tôi sẽ xem xét các đề xuất của bạn để xem liệu tôi có thể triển khai nó theo cách rõ ràng hơn hay không. Cảm ơn bạn đã dẫn đầu! –

7

Cách đơn giản nhất và tự nhiên nhất để làm điều đó là thông qua fomset clean():

class RequireOneFormSet(forms.models.BaseInlineFormSet): 
    def clean(self): 
     super().clean() 
     if not self.is_valid(): 
      return 
     if not self.forms or not self.forms[0].cleaned_data: 
      raise ValidationError('At least one {} required' 
            .format(self.model._meta.verbose_name)) 

class ProfileInline(admin.StackedInline): 
    model = Profile 
    formset = RequireOneFormSet 

(Lấy cảm hứng từ this Matthew Flanagan's snippet và bình luận Mitar của dưới đây, thử nghiệm để làm việc trong Django 1.11 và 2.0).

+2

Hoàn hảo! Tôi đã thay đổi nó một chút, sử dụng 'if not self.is_valid():' thay vì tự đi qua 'self.errors' và sử dụng' self.model._meta.verbose_name'. – Mitar

16

Bây giờ với Django 1.7, bạn có thể sử dụng tham số min_num. Bạn không cần lớp RequiredInlineFormSet nữa.

Xem https://docs.djangoproject.com/en/1.8/ref/contrib/admin/#django.contrib.admin.InlineModelAdmin.min_num

class ProfileInline(admin.StackedInline): 
    """ 
    Allows profile to be added when creating user 
    """ 
    model = Profile 
    extra = 1 
    max_num = 1 
    min_num = 1 # new in Django 1.7 


class UserProfileAdmin(admin.ModelAdmin): 
    """ 
    Options for the admin interface 
    """ 
    inlines = [ProfileInline] 
    ... 


admin.site.register(User, UserProfileAdmin) 
+0

Điều này chỉ kiểm soát số lượng nội tuyến được hiển thị. Nó không yêu cầu bất kỳ thông tin nào được điền vào. – spookylukey

+5

Thực ra nếu nội tuyến có một trong những trường bắt buộc, nó cũng kiểm soát nội tuyến đó được điền vào. – quick

+1

Xác nhận làm việc :) – Geotob

Các vấn đề liên quan