如何保存具有直通关系的多对多字段
Posted
技术标签:
【中文标题】如何保存具有直通关系的多对多字段【英文标题】:How to save a ManyToMany field with a through relationship 【发布时间】:2015-10-30 00:07:43 【问题描述】:我有以下具有ManyToMany
和through
关系的模型:
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.
。我该怎么做?
【问题讨论】:
【参考方案1】:您必须在视图中显式保存MeetingArrival
对象,以在ManyToManyField
带有through
参数的情况下保存中间模型。
对于 Django 2.1 及以下版本,如果 ManyToManyField
带有中间模型,则不能使用普通多对多字段可用的 add
、create
或 assignment
。
根据Django 1.8 docs:
与普通的多对多字段不同,您不能使用 add、create 或 创建关系的任务。
创建这种关系的唯一方法是创建 中间模型的实例。
因此,您必须在视图中显式创建 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
【讨论】:
谢谢,我如何获得your_visitor_object_here
?表单输入是多选。
我们可以编写逻辑来获取视图中的访问者对象。由于Visitor
是外键,因此您可以找到哪个MeetingArrival
与哪个Visitor
相关,然后使用该Visitor
创建一个MeetingArrival
对象。
谢谢。如果我有另一个不是through
字段的ManyToMany
字段,那么我将如何保存表单?例如` meeting_team_members = models.ManyToManyField(Team, blank=False, null=False)`
您只需通过.add()
即可做到这一点。假设ABC
是meeting_team_members
的模型。然后你可以做abc_object.meeting_team_members.add(team_object)
【参考方案2】:
对于 django 1.x,正如 Rahul 所说,您不能使用 add
、create
等
对于 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】:使用到表时,需要手动保存。
MeetingArrival.objects.create( ... )
【讨论】:
谢谢,可以举个例子吗?【参考方案4】:不要将处理 ManyToManyField 的逻辑放在视图中,而是将其放在表单中,这样如果您在多个地方使用表单,就不必重复自己。
为此,您需要重写ModelForm
的save
方法。
查看我对另一个问题的更全面的回答:https://***.com/a/40822731/2863603
【讨论】:
【参考方案5】:我是这样做的,仍然试图从选择中获取多个 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)
【讨论】:
【参考方案6】:user_profiles = UserProfile.objects.all()
NotificationUser.objects.bulk_create([NotificationUser(user=user_profile, notification=notification) for user_profiles in user_profiles])
【讨论】:
以上是关于如何保存具有直通关系的多对多字段的主要内容,如果未能解决你的问题,请参考以下文章