如何保存具有直通关系的 ManyToMany 字段

How to save a ManyToMany field with a through relationship

我有以下具有 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.。我该怎么做?

当您使用 table时,您需要手动保存。

MeetingArrival.objects.create( ... )

如果 ManyToManyField 带有 through 参数,您必须在视图中显式保存 MeetingArrival 对象以保存中间模型。

对于 Django 2.1 及以下版本,如果 ManyToManyField 具有中间模型,则不能使用 addcreateassignment,它们是适用于普通的多对多字段。

根据 Django 1.8 docs:

Unlike normal many-to-many fields, you can’t use add, create, or assignment to create relationships.

The only way to create this type of relationship is to create instances of the intermediate model.

因此,您必须在视图中显式创建一个 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

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

为此,您需要重写ModelFormsave方法。

查看我对另一个问题的更详尽的回答:

我这样做了,仍然试图从 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)

对于 django 1.x,正如 Rahul 所说,您不能使用 addcreate

对于 django 2.x,您实际上可以根据此处的文档 django 2.x

You can also use add(), create(), or set() to create relationships, as long as you specify through_defaults for any required fields:

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

user_profiles = UserProfile.objects.all()

NotificationUser.objects.bulk_create([NotificationUser(user=user_profile, notification=notification) for user_profile in user_profiles])