سؤال

أواجه مشكلة في حفظ بيانات 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() 

كيف يمكنني تهيئة العضوية لجدول العضوية ليتم ملؤها بشكل صحيح؟

هل كانت مفيدة؟

المحلول

في حالة استخدام علاقة M2M العادية (وليس من خلال الجدول الوسيط) يمكنك استبدال:

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

مع

form.save_m2m()

ولكن في حالة استخدام الجداول الوسيطة ، تحتاج إلى معالجة البيانات النشر يدويًا وإنشاء كائنات عضوية مع جميع الحقول المطلوبة (مشكلة مماثلة). الحل الأساسي هو تغيير وجهة نظرك إلى شيء مثل:

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())

لاحظ كيف يتم التلاعب بطلب. post (.getList). هذا لأن المنشور والحصول QueryDict الكائنات التي لها بعض الآثار (request.post [الأعضاء "] سترجع دائمًا كائن واحد!).

يمكنك تعديل هذا الرمز للحصول عليه أكثر موثوقية (معالجة الأخطاء وما إلى ذلك) ، والمزيد من المطوّل ، على سبيل المثال:

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

لكن لاحظ أنك تقوم ببعض استعلامات DB في حلقة ليست فكرة جيدة بشكل عام (من حيث الأداء).

نصائح أخرى

مثل ما فعله 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

تحتاج أيضًا إلى تحديد الفصل الدراسي للعضوية:

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

أعتقد أنه يجب عليك أيضًا الإزالة User في accept = models.BooleanField(User). لا ينبغي أن يكون من الضروري تعيين التاريخ عند الادخار إذا كنت تستخدم auto_now! ولكن ربما يكون `auto_now_add أكثر احتمالا ما تحتاجه (http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.datefield)

هذه هي الطريقة التي فعلت بها في طريقة عرض عامة قائمة على فئة التحديث (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)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top