Django manytomany访问相关专栏

时间:2019-02-20 09:18:08

标签: django many-to-many

我试图弄清楚Django如何理解m2m关系,在SQL中,您只需在中间表中添加一些联接即可。

我有一个包含各种样品的容器。样本可以散布在各种容器中。

因此,在我的容器中,我添加了一个别名样本m2m字段(本质上是另一个表的书签)。

我能做的是获得一个容器并显示表单信息,我想将Sample列添加到表单中,如果我对samples m2m字段执行此操作,它将返回一个多字段,但是我如何访问通过m2m的其他相关字段sample_id> =

class Container(models.Model):
    container_id = models.AutoField(primary_key=True)
    samples = models.ManyToManyField(Sample, through='JoinSampleContainer', through_fields=('container_id', 'sample_id'), related_name='container')
    location_id = models.ForeignKey(Location, db_column='location_id', on_delete = models.PROTECT)
    icon_desc = models.ForeignKey(Icon, db_column='icon_desc', null=True, blank=True, default='Box',on_delete = models.PROTECT)
    container_name = models.CharField(max_length=50, blank=True, null=True)
    container_type = models.CharField(max_length=50, blank=True, null=True)

在我的示例表中,我添加了容器别名,以充当另一个表的书签

class Sample(models.Model):
    sample_id = models.AutoField(primary_key=True)
    containers = models.ManyToManyField(Container, through='JoinSampleContainer', through_fields=('sample_id', 'container_id'), related_name='sample')
    sample_number = models.IntegerField()
    material_type = models.CharField(max_length=200, default='', blank=True, null=True, choices = MATERIALS)
    weight = models.DecimalField(max_digits=6, decimal_places=2)
    description = models.CharField(max_length=500, default='', blank=True, null=True)
    recovery_method = models.CharField(max_length=200, default='', blank=True, null=True, choices = RECOVERY_METHODS)
    comments = models.CharField(max_length=1000, default='', blank=True, null=True)

在这种情况下,我要管理通过表:

class JoinSampleContainer(models.Model):
    id = models.AutoField(primary_key=True)
    container_id = models.ForeignKey(Container, db_column='container_id', on_delete = models.PROTECT)
    sample_id = models.ForeignKey(Sample, db_column='sample_id', on_delete = models.PROTECT)

所以现在我想通过表单显示单个容器的内容。我已经设置了网址以传递container_id。

# views.py
def containercontents(request, pk):
    post = get_object_or_404(Container, pk=pk)
    # objects = Container.samples.all()
    if request.method == "POST":
        form = ContainerContentsForm(request.POST, instance=post)
        if form.is_valid():
            post = form.save(commit=False)
            #post.user = request.user
            #post.datetime = datetime.datetime.now()
            post.save()
            return redirect('allcontainer')
            #, pk=post.pk)
    else:
        form = ContainerContentsForm(instance=post)
    return render(request, 'container/containercontents.html', {'form': form})

表格

# form.py
class ContainerContentsForm(forms.ModelForm):
    class Meta:
        model = Container
        fields = (
        'location_id',
        'container_name',
        'container_type',
        'icon_desc',
        'samples',
        )

样本似乎列出了所有内容,而与容器无关。

然后是HTML

# html
<a href="{% url 'containercontents' pk=container.pk %}" class="btn btn-primary" role="button">contents</a>

传递给:

# html
<div class="">
  {{ form }}
</div>    

1 个答案:

答案 0 :(得分:0)

您的模型定义错误:您不应在两个模型上都定义ManyToManyField,而只能在其中一个模型上定义。因此,请删除containers上的Sample字段,仅将其保留在Container上。将related_name设置为“容器”(复数)。这样,关系Container-> Samplecontainer.samples.all(),相反的关系为sample.containers.all()

现在,表单的用途是允许您选择要与Sample关联的Container。因此,默认情况下,该字段将由ModelMultipleChoiceField表示。使用Sample实例初始化表单时,应预先选择已经关联的Container

您可以通过为字段指定queryset并覆盖以下格式的默认字段来缩小样本范围:

class ContainerContentsForm(forms.ModelForm):
    class Meta:
        # same code here

    samples = forms.ModelMultipleChoiceField(
        queryset = Sample.objects.filter(...)
    )

您说要“通过表单显示Container的内容”,如果只想显示,为什么要使用表单?要仅显示内容,请循环浏览相关样本并显示它们:

{% for sample in form.instance.samples.all %}
    {{ sample.sample_id }}
{% endfor %} 

注意:您应该将ID重命名为idsample.sample_id是不好的编程风格。但是我已经告诉过你了。