【问题标题】:Django m2m form save " through " tableDjango m2m 表单保存“通过”表
【发布时间】:2011-03-05 17:36:32
【问题描述】:

我在保存包含“通过”类表的 m2m 数据时遇到问题。 我想将所有选定的成员(在表单中选择)保存在直通表中。 但我不知道如何在视图中初始化“通过”表。

我的代码:

class Classroom(models.Model):
     user = models.ForeignKey(User, related_name = 'classroom_creator')
     classname = models.CharField(max_length=140, unique = True)
     date = models.DateTimeField(auto_now=True)
     open_class = models.BooleanField(default=True)
     members = models.ManyToManyField(User,related_name="list of invited members", through = 'Membership')

class Membership(models.Model): 
      accept = models.BooleanField(User)
      date = models.DateTimeField(auto_now = True) 
      classroom = models.ForeignKey(Classroom, related_name = 'classroom_membership')
      member = models.ForeignKey(User, related_name = 'user_membership')

在视图中:

def save_classroom(request):
   classroom_instance = Classroom()
   if request.method == 'POST':
        form = ClassroomForm(request.POST, request.FILES, user = request.user) 
        if form.is_valid():
           new_obj = form.save(commit=False)
           new_obj.user = request.user 
           new_obj.save()
           membership = Membership(member = HERE SELECTED ITEMS FROM FORM,classroom=new_obj)

           membership.save() 

我应该如何初始化要填充的 Membership 表的成员资格?

【问题讨论】:

    标签: django forms m2m


    【解决方案1】:

    您还需要为会员指定教室:

    membership = Membership(member = request.user,
                            classroom=new_obj) #if new_obj if your classroom
    membership.save()
    

    我猜你也应该删除accept = models.BooleanField(User) 中的User。如果您使用auto_now,则无需在保存时设置日期!但也许 `auto_now_add 更可能是您需要的 (http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.DateField)

    【讨论】:

    • 谢谢,这似乎是自然的解决方案,问题是 new_obj - 我的教室还没有创建,我猜。现在我在 new_obj.save() 之后添加了上述声明,新的错误是:无法在指定中间模型的 ManyToManyField 上设置值。请改用会员管理器。 :)
    • 然后尝试使用 Membership.objects.create(),但不要忘记指定 classromm 对象!
    • 很抱歉再次提出多年前的问题,但如果您想更新而不是创建记录怎么办?如果记录已经存在,objects.create 不是添加新记录而不是更新吗?
    【解决方案2】:

    如果使用正常的 m2m 关系(不是通过中间表),您可以替换:

    membership = Membership(member = HERE SELECTED ITEMS FROM FORM,classroom=new_obj)
    membership.save()
    

    与

    form.save_m2m()
    

    但在使用中间表的情况下,您需要手动处理 POST 数据并创建包含所有必填字段的 Membership 对象 (similar problem)。最基本的解决方案是将您的视图更改为:

    def save_classroom(request):
        if request.method == 'POST':
            form = ClassroomForm(request.POST, request.FILES)
    
            if form.is_valid():
               new_obj = form.save(commit=False)
               new_obj.user = request.user 
               new_obj.save()
    
               for member_id in request.POST.getlist('members'):
                    membership = Membership.objects.create(member_id = int(member_id), classroom = new_obj)
               return HttpResponseRedirect('/')
        else:
            form = ClassroomForm()
        return render_to_response('save_classroom.html', locals())
    

    注意 request.POST 是如何被操作的 (.getlist)。这是因为 post 和 get 是 QueryDict 对象,具有一些含义(request.POST['members'] 将始终返回一个对象!)。

    您可以修改此代码以使其更可靠(错误处理等),更详细,例如:

    member = get_object_or_404(User, pk = member_id)
    membership = Membership.objects.create(member = member , classroom = new_obj)
    

    但请注意,您正在循环中执行一些数据库查询,这通常不是一个好主意(就性能而言)。

    【讨论】:

    • 就是这样!这真的让我有些头疼,因为我对 python 还很陌生。非常感谢!
    • 问题:如果我想将教室的创建者添加为成员(required.user),并添加到该列表中,我该怎么做?谢谢!
    • 不确定我是否明白你的意思。您可以创建新的会员对象(在循环之外)以在用户和教室之间创建新的连接(membership = Membership.objects.create(member = request.user,classic = new_obj))或者您可以传递额外的 member_id(即 id POST 中的 request.user 和关系用户课堂将使用现有代码(for 循环)创建。对于第二个选项,您需要将 request.user.id 存储在表单的某个隐藏字段中。
    • 是的!就是这样!我在双重声明“会员资格”方面犹豫不决,但这是正确的解决方案!再次感谢!
    • 很抱歉再次提出多年前的问题,但如果您想更新而不是创建记录怎么办?如果记录已经存在,objects.create 不是添加新记录而不是更新吗?
    【解决方案3】:

    这就是我在基于通用 UpdateForm 类的视图 (django 1.8) 中使用 form_valid 方法为类似但不同的应用程序所做的。

    def form_valid(self, form):
        """
        If the form is valid, save the associated model.
        """
        self.object.members.clear()
        self.object = form.save(commit=False)
        self.object.user = self.request.user 
        self.object.save()
    
        list_of_members = form.cleaned_data['members']
    
        ClassRoom.objects.bulk_create([
                Membership(
                    Course=self.object,
                    member=member_person,
                    order=num)
                for num, member_person in enumerate(list_of_members)
            ])
        return super(ModelFormMixin, self).form_valid(form)
    

    【讨论】:

      【解决方案4】:

      和 dzida 一样,但是使用 form.cleaned_data 而不是 request.post:

      def save_classroom(request):
          if request.method == 'POST':
              form = ClassroomForm(request.POST, request.FILES)
      
              if form.is_valid():
                 new_obj = form.save(commit=False)
                 new_obj.user = request.user
                 new_obj.save()
      
                 for member in form.cleaned_data['members'].all():
                      Membership.objects.create(member = member,  classroom = new_obj)
      
                 return HttpResponseRedirect('/')
          else:
              form = ClassroomForm()
          return render_to_response('save_classroom.html', locals())
      

      您还需要考虑一些会员资格可能会被删除,所以:

      def save_classroom(request):
          if request.method == 'POST':
              form = ClassroomForm(request.POST, request.FILES)
      
              if form.is_valid():
                 new_obj = form.save(commit=False)
                 new_obj.user = request.user
                 new_obj.save()
      
                 final_members = form.cleaned_data['members'].all()
                 initial_members = form.initial['members'].all()
      
                 # create and save new members
                 for member in final_members:
                      if member not in initial_members:
                          Membership.objects.create(member = member,  classroom = new_obj)
      
                 # delete old members that were removed from the form
                 for member in initial_members:
                     if member not in final_members:
                         Membership.objects.filter(member = member, classroom = new_obj).delete()
      
                 return HttpResponseRedirect('/')
          else:
              form = ClassroomForm()
          return render_to_response('save_classroom.html', locals())
      

      如果您使用模型表单(例如在通用 CBV 中:form_class=ClassroomForm),请覆盖并将上面的保存逻辑放在 save 方法中,例如:

      ClassroomForm(forms.ModelForm):
          members = ModelMultipleChoiceField(
              queryset=Classroom.objects.all(),
              widget=SelectMultiple
          )
      
          def save(self, commit=True):
              classroom = super().save(commit=False)
                  if commit:
                      classroom.save()
                      if 'members' in self.changed_data:
                         final_members = self.cleaned_data['members'].all()
                         initial_members = self.initial['members']
      
                         # create and save new members
                         for member in final_members:
                              if member not in initial_members:
                                  Membership.objects.create(member = member,  classroom = new_obj)
      
                         # delete old members that were removed from the form
                         for member in initial_members:
                             if member not in final_members:
                                 Membership.objects.filter(member = member, classroom = new_obj).delete()
      
               return classroom
      

      【讨论】:

        猜你喜欢
        • 2016-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-03
        • 2011-05-24
        • 1970-01-01
        • 2011-03-08
        • 2020-07-11
        • 2021-02-15
        相关资源
        最近更新 更多