【问题标题】:Django Generic Views ManyToMany with "through" model带有“通过”模型的 Django 通用视图 ManyToMany
【发布时间】:2016-07-23 06:17:48
【问题描述】:

我的第一个 Django 项目遇到了一些问题,这一定是一个常见/易于解决的项目!作为背景知识,我正在构建一个用于跟踪治疗预约的应用程序。大多数数据结构都相当简单,除了这个需要 ManyToMany 的实例之外,还有一个中间(通过)模型。正如您在下面的models.py 中看到的那样,有三个模型与我遇到的问题相关。 Contact 模型用于存储客户的联系方式。 Case模型是处理一件工作/工作的概念。任何特定案例都可以有多个会话,依此类推。通常情况下,一个案例可能有两个或多个联系人,他们将在他们之间分摊账单。因此,需要使用中间模型的 ManyToMany 来存储该特定联系人将支付的账单百分比。 模型.py

class Contact(models.Model):
    first_name = models.CharField(max_length=200)
    last_name = models.CharField(max_length=200)
class Case(models.Model):
    invoicees = models.ManyToManyField(Contact, through='Invoicees_Members', through_fields=('case','contact'),null=True, blank=True)
class Invoicees_Members(models.Model):
     contact = models.ForeignKey(Contact, on_delete=models.CASCADE)
     case = models.ForeignKey(Case, on_delete=models.CASCADE)
     invoice_percentage = models.IntegerField(validators = [MinValueValidator(1), MaxValueValidator(100)],null=True, blank=True)

我在 stackoverflow 和其他网站上搜索了很多关于如何处理保存表单提交的内容。最常见的解决方案似乎是我在下面尝试实现的。您会注意到我正在尝试使用基于类的通用视图(在本例中为 CreateView)。

views.py

class CaseCreate(CreateView):
    model = Case
    success_url = '/cases/'
    fields = '__all__'
    def form_valid(self, form):
        self.instance = form.save(commit=False)
        for contact in form.cleaned_data['invoicees']:
            invoicee = Invoicees_Members()
            invoicee.case = self.instance
            invoicee.contact = contact
            invoicee.save()
        return super(ModelFormMixin, self).form_valid(form)

不幸的是,表单提交导致以下错误:“异常值:save() 禁止以防止由于未保存的相关对象‘case’而导致数据丢失”。我的假设是由于某种原因 form.save(commit=False) 没有返回用于 Invoicees_Members 模型保存的 ID...

有什么想法吗?我在这里错了一定是微不足道的。附言我尝试使用 self.object 代替 self.instance 并遇到同样的错误。

错误和堆栈跟踪:

Traceback:

File "C:\Python27\lib\site-packages\django\core\handlers\base.py" in get_response
  149.                     response = self.process_exception_by_middleware(e, request)

File "C:\Python27\lib\site-packages\django\core\handlers\base.py" in get_response
  147.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "C:\Python27\lib\site-packages\django\views\generic\base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)

File "C:\Python27\lib\site-packages\django\views\generic\base.py" in dispatch
  88.         return handler(request, *args, **kwargs)

File "C:\Python27\lib\site-packages\django\views\generic\edit.py" in post
  256.         return super(BaseCreateView, self).post(request, *args, **kwargs)

File "C:\Python27\lib\site-packages\django\views\generic\edit.py" in post
  222.             return self.form_valid(form)

File "C:\Users\danie\Documents\django-projects\office_management\officeman\views.py" in form_valid
  40.           invoicee.save()

File "C:\Python27\lib\site-packages\django\db\models\base.py" in save
  651.                         "unsaved related object '%s'." % field.name

Exception Type: ValueError at /cases/add
Exception Value: save() prohibited to prevent data loss due to unsaved related object 'case'.

【问题讨论】:

    标签: python django django-generic-views manytomanyfield


    【解决方案1】:

    可能是ManyToMany关系导致的保存顺序问题。最好先阅读官方文档...

    每次使用commit=False 保存表单时,Django 都会添加一个 save_m2m() 方法添加到您的 ModelForm 子类。手动完成后 保存了表单产生的实例,可以调用save_m2m() 保存多对多的表单数据。

    示例:

    # Create a form instance with POST data.
    >>> f = AuthorForm(request.POST)
    
    # Create, but don't save the new author instance.
    >>> new_author = f.save(commit=False)
    
    # Modify the author in some way.
    >>> new_author.some_field = 'some_value'
    
    # Save the new instance.
    >>> new_author.save()
    
    # Now, save the many-to-many data for the form.
    >>> f.save_m2m()
    

    通过示例阅读保存方法的完整文档Here

    【讨论】:

      【解决方案2】:

      您必须先保存案例模型实例,然后才能保存发票实例。这个错误是有道理的,因为 Django 无法知道案例实例的 ID 以便能够将其作为外键保存在发票模型上。 self.instance = form.save(commit=True)。并查看此链接,Django documentation 上有关于此错误的说明。

      【讨论】:

        猜你喜欢
        • 2012-06-12
        • 2015-05-15
        • 2017-12-24
        • 1970-01-01
        • 2014-12-08
        • 2018-04-05
        • 2016-06-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多