How do I filter ForeignKey choices in a Django ModelForm?

前端 未结 7 1703
感情败类
感情败类 2020-11-22 09:07

Say I have the following in my models.py:

class Company(models.Model):
   name = ...

class Rate(models.Model):
   company = models.ForeignKey(C         


        
7条回答
  •  伪装坚强ぢ
    2020-11-22 09:44

    This is simple, and works with Django 1.4:

    class ClientAdminForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            super(ClientAdminForm, self).__init__(*args, **kwargs)
            # access object through self.instance...
            self.fields['base_rate'].queryset = Rate.objects.filter(company=self.instance.company)
    
    class ClientAdmin(admin.ModelAdmin):
        form = ClientAdminForm
        ....
    

    You don't need to specify this in a form class, but can do it directly in the ModelAdmin, as Django already includes this built-in method on the ModelAdmin (from the docs):

    ModelAdmin.formfield_for_foreignkey(self, db_field, request, **kwargs)¶
    '''The formfield_for_foreignkey method on a ModelAdmin allows you to 
       override the default formfield for a foreign keys field. For example, 
       to return a subset of objects for this foreign key field based on the
       user:'''
    
    class MyModelAdmin(admin.ModelAdmin):
        def formfield_for_foreignkey(self, db_field, request, **kwargs):
            if db_field.name == "car":
                kwargs["queryset"] = Car.objects.filter(owner=request.user)
            return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
    

    An even niftier way to do this (for example in creating a front-end admin interface that users can access) is to subclass the ModelAdmin and then alter the methods below. The net result is a user interface that ONLY shows them content that is related to them, while allowing you (a super-user) to see everything.

    I've overridden four methods, the first two make it impossible for a user to delete anything, and it also removes the delete buttons from the admin site.

    The third override filters any query that contains a reference to (in the example 'user' or 'porcupine' (just as an illustration).

    The last override filters any foreignkey field in the model to filter the choices available the same as the basic queryset.

    In this way, you can present an easy to manage front-facing admin site that allows users to mess with their own objects, and you don't have to remember to type in the specific ModelAdmin filters we talked about above.

    class FrontEndAdmin(models.ModelAdmin):
        def __init__(self, model, admin_site):
            self.model = model
            self.opts = model._meta
            self.admin_site = admin_site
            super(FrontEndAdmin, self).__init__(model, admin_site)
    

    remove 'delete' buttons:

        def get_actions(self, request):
            actions = super(FrontEndAdmin, self).get_actions(request)
            if 'delete_selected' in actions:
                del actions['delete_selected']
            return actions
    

    prevents delete permission

        def has_delete_permission(self, request, obj=None):
            return False
    

    filters objects that can be viewed on the admin site:

        def get_queryset(self, request):
            if request.user.is_superuser:
                try:
                    qs = self.model.objects.all()
                except AttributeError:
                    qs = self.model._default_manager.get_queryset()
                return qs
    
            else:
                try:
                    qs = self.model.objects.all()
                except AttributeError:
                    qs = self.model._default_manager.get_queryset()
    
                if hasattr(self.model, ‘user’):
                    return qs.filter(user=request.user)
                if hasattr(self.model, ‘porcupine’):
                    return qs.filter(porcupine=request.user.porcupine)
                else:
                    return qs
    

    filters choices for all foreignkey fields on the admin site:

        def formfield_for_foreignkey(self, db_field, request, **kwargs):
            if request.employee.is_superuser:
                return super(FrontEndAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
    
            else:
                if hasattr(db_field.rel.to, 'user'):
                    kwargs["queryset"] = db_field.rel.to.objects.filter(user=request.user)
                if hasattr(db_field.rel.to, 'porcupine'):
                    kwargs["queryset"] = db_field.rel.to.objects.filter(porcupine=request.user.porcupine)
                return super(ModelAdminFront, self).formfield_for_foreignkey(db_field, request, **kwargs)
    

提交回复
热议问题