Django - 使用Formsets在没有通过表的2个模型之间建立多对多的关系

时间:2022-10-05 14:21:25

I have a model Attribute and Product, declared like this:

我有一个模型属性和产品,声明如下:

class Attribute(models.Model):
    value = models.TextField()
    owner = models.ForeignKey(User)
    type = models.ForeignKey(AttributeType)     
    image = ImageField(upload_to='attributes', null=True, blank=True)     
    related_attribute = models.ManyToManyField('self', blank = True, null = True) 

class BaseWorkspace(models.Model):
    name = models.CharField(max_length=255)
    owner = models.ForeignKey(User)
    attributes = models.ManyToManyField('Attribute', blank = True, null = True)
    created = CreationDateTimeField()
    modified = ModificationDateTimeField()
    comments = models.ManyToManyField('Comment', blank = True, null = True )
    sort_order = models.IntegerField(blank = True)

class Product(BaseWorkspace):
    project = models.ForeignKey('Project', related_name='products')

how can I establish m-m relationship using formsets? I have tried model formset factories like this:

如何使用formset建立m-m关系?我尝试过像这样的模型formset工厂:

AttributeFormset = modelformset_factory(Attribute, form=AttributeForm)

with this function in the generic view:

在通用视图中使用此函数:

def form_valid(self, form):
        f = form.instance
        f.sort_order = Product.default_sort_order()
        f.owner = self.request.user
        f.project = get_object_or_404(Project, pk=self.kwargs['pk'])
        context = self.get_context_data()
        attribute_form = context['attribute_form']
        if attribute_form.is_valid():
            self.object = form.save()
            attribute_form.instance = self.object
            attribute_form.save()
            return HttpResponseRedirect(reverse(self.get_success_url()))
        else:
            return self.render_to_response(self.get_context_data(form=form))

but I cannot get it to work. any ideas?

但我无法让它发挥作用。有任何想法吗?

2 个解决方案

#1


0  

Try something like this:

尝试这样的事情:

from django.forms.models import modelformset_factory
def my_view_function(request) :

    # not sure where the product whose formset we are working on comes from
    product = <whatever>

    AttributeFormSet = modelformset_factory(Attribute)

    if request.method == "POST" :
        # POST bound formset
        formset = AttributeFormSet(request.POST, queryset=Attribute.objects.filter(product=product))
        # If the entire formset is valid
        if formset.is_valid() :
            for form in formset:
                # Save each form in the set
                b = form.save()
        else : 
            #There was an error (add a message using the messages framework?)
            pass
    else :
        # initial formset w/o post
        formset = AttributeFormSet(queryset=Attribute.objects.filter(product=product))

    ...

Its kind of hard to give you more specific answer, I think we would need the entire view function or view class if you are using class based views.

它很难给你更具体的答案,我认为如果你使用基于类的视图我们将需要整个视图函数或视图类。

In your template, something as simple as this (from the docs) should do it.

在您的模板中,一些简单的东西(来自文档)应该这样做。

<form method="post" action="">
    {{ formset.management_form }}
    <table>
        {% for form in formset %}
        {{ form }}
        {% endfor %}
    </table>
</form>

If you need the ability to add forms to the formset at runtime w/ javascript look at this: http://code.google.com/p/django-dynamic-formset/. Ive never used it, but at the very least it looks like a step in the correct direction.

如果您需要能够在运行时将表单添加到formset w / javascript,请查看:http://code.google.com/p/django-dynamic-formset/。我从未使用它,但至少看起来它是朝着正确方向迈出的一步。

EDIT

First exclude product from the formset

首先从formset中排除产品

AttributeFormSet = modelformset_factory(Attribute, exclude=('product',))

then change the form processing block to not commit on save, and manually attach the product.

然后将表单处理块更改为不提交保存,并手动附加产品。

        if formset.is_valid() :
            for form in formset:
                # get this form's instance
                b = form.save(commit=False)
                # attach product
                b.product = product
                # save the instance
                b.save()

#2


0  

By using f = form.instance you access the original instance. If the attribute_form is valid you call the save method on the form, instead of the f. All the changes you did on f will be lost.

通过使用f = form.instance,您可以访问原始实例。如果attribute_form有效,则在表单上调用save方法,而不是f。您在f上所做的所有更改都将丢失。

Have a look at saving-objects-in-the-formset how to update instances of a formset before saving them.

看一下save-objects-in-formset如何在保存之前更新formset的实例。

#1


0  

Try something like this:

尝试这样的事情:

from django.forms.models import modelformset_factory
def my_view_function(request) :

    # not sure where the product whose formset we are working on comes from
    product = <whatever>

    AttributeFormSet = modelformset_factory(Attribute)

    if request.method == "POST" :
        # POST bound formset
        formset = AttributeFormSet(request.POST, queryset=Attribute.objects.filter(product=product))
        # If the entire formset is valid
        if formset.is_valid() :
            for form in formset:
                # Save each form in the set
                b = form.save()
        else : 
            #There was an error (add a message using the messages framework?)
            pass
    else :
        # initial formset w/o post
        formset = AttributeFormSet(queryset=Attribute.objects.filter(product=product))

    ...

Its kind of hard to give you more specific answer, I think we would need the entire view function or view class if you are using class based views.

它很难给你更具体的答案,我认为如果你使用基于类的视图我们将需要整个视图函数或视图类。

In your template, something as simple as this (from the docs) should do it.

在您的模板中,一些简单的东西(来自文档)应该这样做。

<form method="post" action="">
    {{ formset.management_form }}
    <table>
        {% for form in formset %}
        {{ form }}
        {% endfor %}
    </table>
</form>

If you need the ability to add forms to the formset at runtime w/ javascript look at this: http://code.google.com/p/django-dynamic-formset/. Ive never used it, but at the very least it looks like a step in the correct direction.

如果您需要能够在运行时将表单添加到formset w / javascript,请查看:http://code.google.com/p/django-dynamic-formset/。我从未使用它,但至少看起来它是朝着正确方向迈出的一步。

EDIT

First exclude product from the formset

首先从formset中排除产品

AttributeFormSet = modelformset_factory(Attribute, exclude=('product',))

then change the form processing block to not commit on save, and manually attach the product.

然后将表单处理块更改为不提交保存,并手动附加产品。

        if formset.is_valid() :
            for form in formset:
                # get this form's instance
                b = form.save(commit=False)
                # attach product
                b.product = product
                # save the instance
                b.save()

#2


0  

By using f = form.instance you access the original instance. If the attribute_form is valid you call the save method on the form, instead of the f. All the changes you did on f will be lost.

通过使用f = form.instance,您可以访问原始实例。如果attribute_form有效,则在表单上调用save方法,而不是f。您在f上所做的所有更改都将丢失。

Have a look at saving-objects-in-the-formset how to update instances of a formset before saving them.

看一下save-objects-in-formset如何在保存之前更新formset的实例。