如何在 django 的 CreateView 中使用两个不同的模型

How to use two different models in CreateView in django

我有以下型号:

class BankAccount(models.Model):
    owner = models.ForeignKey(User)

class MoneyTransfer(models.Model):
    sender = models.ForeignKey(BankAccount)

和url:

url(r'^accounts/(?P<pk>\w+)/send_transfer$', SendTransfer.as_view(), name='SendTransfer')

表示"I want to send money from Account with id=pk"

这是我的观点:

class SendTransfer(View):
    form_class = SendTransferForm
    template_name = 'dashboard/send_transfer.html'

    def get(self, request, *args, **kwargs):
        instance = BankAccount.objects.get(id=self.kwargs['pk'])
        if instance.is_legal():
            if instance.organization.owners.all().filter(user__id=self.request.user.id).count() == 0:
                return None
        else:
            if instance.citizen.user.id != self.request.user.id:
                return None
        return render(self.request, self.template_name, self.get_context_data())

    def post(self, request, *args, **kwargs):
        sender = BankAccount.objects.get(id=kwargs['pk'])

        form = self.form_class(sender, self.request.user, request.POST)
        if form.is_valid():
            MoneyTransfer.objects.create(sender=sender,
                                     receiver=BankAccount.objects.get(id=self.request.POST['receiver']),
                                     total=float(self.request.POST['total']),
                                     when=timezone.localtime(timezone.now()),
                                     comment=self.request.POST['comment'])
            return redirect('AccountDetail', kwargs['pk'])

        data = self.get_context_data()
        data['form'] = form
        return render(request, self.template_name, data)

    def get_context_data(self):
        account = BankAccount.objects.get(id=self.kwargs['pk'])
        return {'form': SendTransferForm(account, self.request.user),
            'user': self.request.user,
            'account': account}

我认为 CBV 有很多冗余代码。我可以做些什么来缩短它?

UPD

我当前的代码:

class SendTransfer(SingleObjectMixin, FormView):
model = BankAccount
form_class = SendTransferForm
template_name = 'dashboard/send_transfer.html'

def dispatch(self, request, *args, **kwargs):
    self.object = self.get_object()
    return super(SendTransfer, self).dispatch(request, *args, **kwargs)

def get_object(self, queryset=None):
    obj = super(SendTransfer, self).get_object(queryset)

    if obj.is_legal():
        if not obj.organization.owners.filter(user=self.request.user).exists():
            raise Http404
    else:
        if obj.citizen.user != self.request.user:
             raise Http404

    return obj

def form_valid(self, form):
    data = form.cleaned_data
    MoneyTransfer.objects.create(sender=self.object,
                                 receiver=data['receiver'], # ModelChoiceField in the form
                                 total=data['total'], # FloatField in the form, etc.
                                 when=timezone.localtime(timezone.now()),
                                 comment=data['comment'])
    return redirect('AccountDetail', self.object.pk)

dispatch() 方法的最后一行引发 TypeError: init() takes at least 3 arguments (1 given)

CBV 专为代码重用而设计。如果您还没有另一个 class 可以从您发布的代码中获益,那么实际的代码量几乎是相同的,无论是 CBV 还是普通函数。

但是更 pythonic 和 Django-ish(从我有偏见的 POV)的方式是:

  1. FormView 而不是 View 继承您的 class。这稍微简化了表单管理。
  2. 添加一个 SingleObjectMixin 以免费从 url kwargs 获取对象。
  3. 将对象验证移至 get_object() 方法。如果您的对象未通过验证,则引发 404 是一个很好的做法。
  4. 重构 get_context_data(),因为您已经在上下文中拥有所有这些数据(requestformobject
  5. 不要依赖 self.request.POST,而是通过表单清理数据。

    class SendTransfer(SingleObjectMixin, FormView):
        model = BankAccount
        form_class = SendTransferForm
        template_name = 'dashboard/send_transfer.html'
    
        def dispatch(self, request, *args, **kwargs):
            self.object = self.get_object()
            return super(SendTransfer).dispatch(request, *args, **kwargs)
    
        def get_object(self, queryset=None):
            obj = super(SendTransfer, self).get_object(queryset)
    
            if obj.is_legal():
                if not obj.organization.owners.filter(user=self.request.user).exists():
                    raise Http404
            else:
                if obj.citizen.user != self.request.user:
                     raise Http404
    
            return obj
    
        def form_valid(self, form):
            data = form.cleaned_data
            MoneyTransfer.objects.create(sender=self.object,
                                         receiver=data['receiver'], # ModelChoiceField in the form
                                         total=data['total'], # FloatField in the form, etc.
                                         when=timezone.localtime(timezone.now()),
                                         comment=data['comment'])
            return redirect('AccountDetail', self.object.pk)
    

由于 CBV 魔法,您的一些代码已经消失,一些只是转移到其他方法。看看吧,欢迎大家多多指教