如何使用直通关系保存ManyToMany字段

时间:2015-08-06 17:54:06

标签: django django-models django-forms django-views many-to-many

我有以下具有ManyToManythrough关系的模型:

class Meeting(models.Model):
    site = models.ForeignKey(Site)
    meeting_title = models.CharField(default='', max_length=128, blank=True, null=True)
    meeting_visitors = models.ManyToManyField(Visitor, through="MeetingArrival", blank=False, null=False) 

class Visitor(models.Model):
    visitor_company = models.ForeignKey(Company)
    visitor_name = models.CharField(default='', max_length=128, blank=False, null=False)

class MeetingArrival(models.Model):
    visitor = models.ForeignKey(Visitor)
    meeting = models.ForeignKey(Meeting)
    arrival_status = models.BooleanField(default=False)

我有一个表单来创建会议:

class AddMeetingForm(forms.ModelForm):

    class Meta:
        model = Meeting
        exclude = ['site',]

保存表单的简单视图:

def add_meeting(request): 
    add_meeting_form = AddMeetingForm(request.POST or None)
    site = Site.objects.get(user=request.user.id)

    if request.method == "POST":
        if add_meeting_form.is_valid():

            obj = add_meeting_form.save(commit=False)
            obj.site = site
            obj.save()

这会保存表单,但不保存meeting_visitors字段,即使此字段在视图中完美呈现。我如何保存这种关系?

修改

如果我将add_meeting_form.save_m2m()添加到视图中,则会获得Cannot set values on a ManyToManyField which specifies an intermediary model. Use meetings.MeetingArrival's Manager instead.。我该怎么做?

6 个答案:

答案 0 :(得分:11)

您需要在视图中明确保存MeetingArrival对象,以便在ManyToManyFieldthrough参数的情况下保存中间模型。

如果ManyToManyField有中间模型,则您无法使用普通多对多字段中可用的addcreateassignment

根据Django docs:

  

与普通的多对多字段不同,您无法使用添加,创建或   分配以建立关系。

     

创建此类关系的唯一方法是创建   中间模型的实例。

因此,您必须在视图中明确创建MeetingArrival对象。

您可以通过以下方式完成:

def add_meeting(request): 
    add_meeting_form = AddMeetingForm(request.POST or None)
    site = Site.objects.get(user=request.user.id)

    if request.method == "POST":
        if add_meeting_form.is_valid():
            obj = add_meeting_form.save(commit=False)
            obj.site = site
            obj.save()

            # create an instance of 'MeetingArrival' object
            meeting_arrival_obj = MeetingArrival(meeting=obj, visitor=<your_visitor_object_here>, arrival_status=True)
            meeting_arrival_obj.save() # save the object in the db

答案 1 :(得分:3)

使用表时,需要手动保存。

MeetingArrival.objects.create( ... )

答案 2 :(得分:1)

对于Django 1.x,就像Rahul所说的,您不能使用addcreate

对于django 2.x,实际上您可以在django 2.x

中查看每个文档
  

您还可以使用add(),create()或set()创建关系,只要您为任何必填字段指定through_defaults即可:

beatles.members.add(john, through_defaults={'date_joined': date(1960, 8, 1)})
beatles.members.create(name="George Harrison", through_defaults={'date_joined': date(1960, 8, 1)})
beatles.members.set([john, paul, ringo, george], through_defaults={'date_joined': date(1960, 8, 1)})

答案 3 :(得分:0)

不要将逻辑放在视图中处理ManyToManyField的位置,而是将其放在表单中,这样如果在多个位置使用表单,就不必重复自己。

为此,您需要覆盖save的{​​{1}}方法。

请参阅我对其他问题的更全面的回答:https://stackoverflow.com/a/40822731/2863603

答案 4 :(得分:0)

我是这样做的,仍然试图从select中获取多个id值,我认为我的javascript中有错误

但这里是python代码:

class ArticuloCreateView(View):
    def __init__(self):
        self.template_name = 'articulo/formulario.html'

    def get(self, request):
        formulario = ArticuloForm()
        contexto = {
            'form': formulario,
            'operation': "Nuevo"
        }
        return render(request, self.template_name, contexto)

    @transaction.atomic
    def post(self, request):
        punto_transaccion = transaction.savepoint()
        formulario = ArticuloForm(request.POST)
        almacenes = request.POST.get('almacenes', 0)
        almacenes = Almacen.objects.filter(id=almacenes)

        if formulario.is_valid():
            datos_formulario = formulario.cleaned_data
            articulo = Articulo()
            articulo.clave = datos_formulario.get('clave')
            articulo.descripcion = datos_formulario.get('descripcion')
            articulo.tipo = datos_formulario.get('tipo')
            articulo.udm = datos_formulario.get('udm')
            articulo.clave_jde = datos_formulario.get('clave_jde')
            articulo.save()

            for almacen in almacenes:
                Stock.objects.create(articulo=articulo, almacen=almacen)
            if punto_transaccion:
                transaction.savepoint_commit(punto_transaccion)

            return redirect(
                reverse('inventarios.articulos_lista')
            )
        contexto = {
            'form': formulario,
        }

        return render(request, self.template_name, contexto)

答案 5 :(得分:0)

user_profiles = UserProfile.objects.all()

NotificationUser.objects.bulk_create([[user_profiles中的user_profile的NotificationUser(user = user_profile,notification = notification)])