2016-10-22 96 views
1

我有一个名为projects的应用程序。其中一个字段为contributors,其中用户列表以ManyToManyField存储。我试图使用户可以在列表中添加和删除用户。使用ModelChoiceField与所有用户的传递查询集一起添加已相当简单。从列表中删除用户仍然没有。我想了解的是如何以及在哪里传递额外的参数,以便我可以在窗体中处理查询,因此它只会列出来自特定项目的用户。Django中ManyToManyField的具有特定参数的窗体中自定义查询

添加用户

models.py

# Model for projects 
class Project(models.Model): 
    ... 
    contributors = models.ManyToManyField(User, blank=True) 
    ... 

forms.py

class AddUserForm(forms.Form): 
    user = forms.ModelChoiceField(queryset=User.objects.all()) 

    class Meta: 
     model = Project 
     fields = [ 
      "user", 
     ] 

views.py

# Add task to a project 
@login_required() 
def projects_adduser(request, id): 

    # Fetch the project if it exists 
    project = get_object_or_404(Project, id=id) 

    # Form for adding users to contributors list 
    form = AddUserForm(request.POST or None) 

    # Validate the form 
    if form.is_valid(): 
     user = form.cleaned_data.get("user") 
     project.contributors.add(user) 
     project.save() 
     messages.success(request, "User successfully added to project!") 
     return HttpResponseRedirect(project.get_edit_url()) 

    # Context dict to return for template 
    context = { 
     "title": "Add user to project: " + project.title, 
     "form": form, 
     "instance": project, 
    } 
    return render(request, template + '/form.html', context) 

回答

3

您可以通过初始用户查询集,形成这样的:

form = AddUserForm() 
form.fields["user"].queryset = User.objects.filter(project=project) 

而且,你不需要调用project.save()project.contributors.add(user)已经进行查询并增加M2M连接

+0

酷!我没有想出这个。我自己测试了它。它要简单得多,而且似乎更好。谢谢,我也学到了! – Jayground

+0

回复你删除的问题)它会返回所有在Project模型中与项目连接的用户。 SQL查询将如下所示:'SELECT * FROM“auth_user”INNER JOIN“projects_project”ON(“auth_user”。“id”=“projects_project”。“user_id”)WHERE“projects_project”。“id”= 1' – devxplorer

+0

谢谢,这是迄今为止最简单的解决方案,正是我所期待的。它实际上帮助我从views.py中移除了一些函数并简化了代码。我不知道你可以从forms.py之外操作窗体,我正在寻找如何将参数传递给forms.py中的窗体本身。呃...新手,对吧? :P – Ablivion

2

我决定帮助那些在本周末使用Django的人。我通常在这里发布问题。我认为是时候给人们答复了。这是我的开始!精彩!我使用Django WebFramework的时间不到2个月。所以我的答案可能还不够,或者还有其他更好的方法。但我以这种方式解决你的问题。 (连我自己测试用我的笔记本电脑与人书面代码!)

我的示例代码是在这里

models.py

from django.db import models 
from django.contrib.auth.models import User 

# Create your models here. 
class Project(models.Model): 
    contributors = models.ManyToManyField(User, blank=True) 

views.py

from django.shortcuts import render, get_object_or_404 
from django.http import HttpResponseRedirect 
from barrierfree.models import Project 
from barrierfree.forms import AddUserForm 

# Create your views here. 
def projects_adduser(request, id): 

    # Fetch the project if it exists 
    project = get_object_or_404(Project, pk=id) 
    pk_number_project = project.pk 
    # Form for adding users to contributors list 
    form = AddUserForm(request.POST, project=pk_number_project) 

    # Validate the form 
    if form.is_valid(): 
     user = form.cleaned_data.get("user") 
     print ("validated!") 
     return HttpResponseRedirect('/Sucess')  
    # Context dict to return for template 

    return render(request, 'test.html', {'form':form}) 

网址。 py

from django.conf.urls import url 
from . import views 
urlpatterns = [ 
    url(r'^test/$', views.projects_adduser, {'id':1}, name='test'), 
] 

的test.html

<form action="." method="post">{% csrf_token %} 
{{ form.as_p }} 
<input type="submit" value="OK"> 
</form> 

forms.py

from django import forms 
from django.contrib.auth.models import User 
from barrierfree.models import Project 

class AddUserForm(forms.Form): 
    user = forms.ModelChoiceField(queryset=None) 

    class Meta: 
     model = Project 
     fields = [ 
      "user", 
     ] 

    def __init__(self, *args, **kwargs): 
     pk_num_project = kwargs.pop('project', None) 
     print (pk_num_project) 
     super(AddUserForm, self).__init__(*args, **kwargs) 
     self.fields['user'].queryset = User.objects.all().filter(project__pk=pk_num_project) 

这里是如何做到这一点的解释。

当我读你的代码时,似乎你通过视图的参数传递了特定项目的pk编号。因为你写了def projects_adduser(request, id):。所以我在url中给了pk = 1来测试,如url(r'^test/$', views.projects_adduser, {'id':1}, name='test'),然后当你启动窗体实例时,你将传递与pk编号匹配的项目实例。 form = AddUserForm(request.POST, project=pk_number_project)那么你将只显示在这个项目中的用户。

def __init__(self, *args, **kwargs): 
     pk_num_project = kwargs.pop('project', None) 
     print (pk_num_project) 
     super(AddUserForm, self).__init__(*args, **kwargs) 
     self.fields['user'].queryset = User.objects.all().filter(project__pk=pk_num_project) 

然后初始化打算什么数据通过重写初始化方法形式展现。

queryset = User.objects.all().filter(project__pk=pk_num_project) 

你可以这样过滤,因为用户和项目是N:N的关系。

我希望我能正确理解你的问题,并给你适当的答案:)我们的旅程与Django祝你好运!和平!

+0

你好,谢谢你的回复。我测试了你的答案,但是我去了devxplorer,因为它更简单。虽然在这个特定的例子中并不需要,但这实际上可能会在诸如论坛的未来应用中派上用场。 – Ablivion

+0

是的,这是真的,devxplorer的答案更简单,更好。我还了解到,有简单的方法@devxplorer,但如果你使用基于类的视图的通用形式呢?我认为这将是不同的情况。我认为你需要使用get_form_kwargs和init方法或更多来做到这一点。你不是吗? – Jayground