说我的我有以下内容models.py
:
class Company(models.Model): name = ... class Rate(models.Model): company = models.ForeignKey(Company) name = ... class Client(models.Model): name = ... company = models.ForeignKey(Company) base_rate = models.ForeignKey(Rate)
即有多个Companies
,每个都有Rates
和Clients
.每个都Client
应该有一个Rate
从它的父级Company's Rates
而不是另一个基础中选择的基础Company's Rates
.
在创建用于添加a的表单时Client
,我想删除Company
选项(因为已经通过Company
页面上的"添加客户端"按钮Rate
选择了该选项)并且还将选择限制为该选项Company
.
我如何在Django 1.0中解决这个问题?
我目前的forms.py
文件只是样板文件:
from models import * from django.forms import ModelForm class ClientForm(ModelForm): class Meta: model = Client
这views.py
也是基本的:
from django.shortcuts import render_to_response, get_object_or_404 from models import * from forms import * def addclient(request, company_id): the_company = get_object_or_404(Company, id=company_id) if request.POST: form = ClientForm(request.POST) if form.is_valid(): form.save() return HttpResponseRedirect(the_company.get_clients_url()) else: form = ClientForm() return render_to_response('addclient.html', {'form': form, 'the_company':the_company})
在Django 0.96中,我能够在渲染模板之前通过执行以下操作来破解它:
manipulator.fields[0].choices = [(r.id,r.name) for r in Rate.objects.filter(company_id=the_company.id)]
ForeignKey.limit_choices_to
似乎有希望,但我不知道如何传入the_company.id
,我不清楚是否这将在Admin界面之外工作.
谢谢.(这似乎是一个非常基本的要求,但如果我重新设计一些东西,我会接受建议.)
ForeignKey由django.forms.ModelChoiceField表示,它是ChoiceField,其选择是模型QuerySet.请参阅ModelChoiceField的参考.
因此,为字段的queryset
属性提供QuerySet .取决于您的表单是如何构建的.如果您构建一个显式表单,您将拥有直接命名的字段.
form.rate.queryset = Rate.objects.filter(company_id=the_company.id)
如果采用默认的ModelForm对象, form.fields["rate"].queryset = ...
这在视图中明确完成.没有黑客攻击.
除了S.Lott的答案和在评论中提到的BecomeGuru之外,还可以通过覆盖ModelForm.__init__
函数来添加查询集过滤器.(这可以很容易地应用于常规表单)它可以帮助重用并保持视图功能整洁.
class ClientForm(forms.ModelForm): def __init__(self,company,*args,**kwargs): super (ClientForm,self ).__init__(*args,**kwargs) # populates the post self.fields['rate'].queryset = Rate.objects.filter(company=company) self.fields['client'].queryset = Client.objects.filter(company=company) class Meta: model = Client def addclient(request, company_id): the_company = get_object_or_404(Company, id=company_id) if request.POST: form = ClientForm(the_company,request.POST) #<-- Note the extra arg if form.is_valid(): form.save() return HttpResponseRedirect(the_company.get_clients_url()) else: form = ClientForm(the_company) return render_to_response('addclient.html', {'form': form, 'the_company':the_company})
如果您在许多模型上需要通用过滤器(通常我声明一个抽象的Form类),这对重用来说很有用.例如
class UberClientForm(ClientForm): class Meta: model = UberClient def view(request): ... form = UberClientForm(company) ... #or even extend the existing custom init class PITAClient(ClientForm): def __init__(company, *args, **args): super (PITAClient,self ).__init__(company,*args,**kwargs) self.fields['support_staff'].queryset = User.objects.exclude(user='michael')
除此之外,我只是重述Django博客材料,其中有许多好的博客材料.
这很简单,适用于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 ....
您不需要在表单类中指定它,但可以直接在ModelAdmin中指定它,因为Django已在ModelAdmin上包含此内置方法(来自文档):
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)
更简单的方法(例如,创建用户可以访问的前端管理界面)是继承ModelAdmin,然后更改下面的方法.最终结果是一个用户界面,它只显示与它们相关的内容,同时允许您(超级用户)查看所有内容.
我已经覆盖了四种方法,前两种方法使得用户无法删除任何内容,并且还删除了管理站点中的删除按钮.
第三个覆盖过滤任何包含引用的查询(在示例中为'user'或'porcupine'(仅作为说明).
最后一个覆盖过滤模型中的任何foreignkey字段,以过滤与基本查询集相同的可用选项.
通过这种方式,您可以提供一个易于管理的前端管理站点,允许用户使用自己的对象,并且您不必记住键入我们上面讨论过的特定ModelAdmin过滤器.
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)
删除"删除"按钮:
def get_actions(self, request): actions = super(FrontEndAdmin, self).get_actions(request) if 'delete_selected' in actions: del actions['delete_selected'] return actions
防止删除权限
def has_delete_permission(self, request, obj=None): return False
筛选可在管理站点上查看的对象:
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
筛选管理站点上所有foreignkey字段的选项:
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)
要使用通用视图执行此操作,例如CreateView ...
class AddPhotoToProject(CreateView): """ a view where a user can associate a photo with a project """ model = Connection form_class = CreateConnectionForm def get_context_data(self, **kwargs): context = super(AddPhotoToProject, self).get_context_data(**kwargs) context['photo'] = self.kwargs['pk'] context['form'].fields['project'].queryset = Project.objects.for_user(self.request.user) return context def form_valid(self, form): pobj = Photo.objects.get(pk=self.kwargs['pk']) obj = form.save(commit=False) obj.photo = pobj obj.save() return_json = {'success': True} if self.request.is_ajax(): final_response = json.dumps(return_json) return HttpResponse(final_response) else: messages.success(self.request, 'photo was added to project!') return HttpResponseRedirect(reverse('MyPhotos'))
最重要的部分......
context['form'].fields['project'].queryset = Project.objects.for_user(self.request.user)
,在这里阅读我的帖子