Django外键与多个类有关

时间:2013-06-18 18:18:48

标签: python django foreign-keys duck-typing

我有两个类,一个叫phase1,一个叫phase2phase2中有phase1个实例(作为外键关系)。现在我想创建另一个名为POObject的类,它具有一个名为purchase的外键值, 一个phase1或一个phase2个对象。也就是说,我希望能够创建POObject purchase设置为phase1实例,然后将其更改为phase2实例。

我相信这叫做Duck Typing,但我不确定在Django中是否有一种简单的方法。我知道Python很容易让Duck打字,但我猜测数据库不会允许它,至少没有一些额外的工作。

有关如何做到这一点的任何建议将不胜感激。

在实施orlenko的建议后,我无法添加新项目。以下是处理创建新实例的视图。它的工作方式是在purchase中创建新的new,然后使用purchasenew2的实例创建POObject

def new(request):
  if request.method == 'POST':
    form = PurchaseOrderForm(request.POST)
    if form.is_valid():
      new_po = form.save()
      return HttpResponseRedirect('/workflow/%s' %new_po.request_number)
    else: 
      return render(request, 'input.html', {'input_type': 'Purchase Order','formset': form, 'error': True})
  else: 
    form = PurchaseOrderForm()
    return render(request, 'input.html', {'input_type': 'Purchase Order','formset': form,})

def new2(request, number):
  po=PurchaseOrder.objects.get(pk=number)
  if request.method == 'POST':
    form = WorkflowForm(request.POST, initial={'purchase': PurchaseOrder.objects.get(pk=number), 'state': 'request'})
    if form.is_valid():
      new_flow = form.save()
      return HttpResponse('Good')
    else:
      print form.errors
      return render(request, 'new-workflow.html', {'form': form, 'purchase': po})
  else:
    form = WorkflowForm(initial={'purchase': PurchaseOrder.objects.get(pk=number), 'state': 'request'})
    return render(request, 'new-workflow.html', {'form': form, 'purchase': po, 'type': 'New'})

适当的表格类是:

class WorkflowForm(ModelForm):
  purchase1=forms.ModelChoiceField(queryset=PurchaseOrder.objects.all(), required=False)
  purchase2=forms.ModelChoiceField(queryset=Phase2.objects.all(), required=False)
  details = forms.CharField(required=False)

  class Meta:
    model = POObject

2 个答案:

答案 0 :(得分:1)

我认为Lalo的建议是正确的。您可以尝试Django content types

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic

class Phase1(models.Model):
    #...

class Phase2(models.Model):
    #...

class POObject(models.Model):
    # Standard names.
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    # Your 'generic' foreign key. 
    purchase = generic.GenericForeignKey('content_type', 'object_id')

然后,你可以这样做:

phase1 = Phase1.objects.get(id='xyz')
phase2 = Phase2.objects.get(id='abc')
p1 = POObject(purchase=phase1)
p1.save()
p2 = POObject(purchase=phase2)
p2.save()

您必须在django.contrib.contenttypes设置中加入INSTALLED_APPS(默认情况下会包含此设置)。

答案 1 :(得分:0)

模型类比其他Python代码更具限制性,因为它们必须转换为实际的数据库表以及它们之间的关系。

但你的想法是"灵活的FK"可以像这样实现:

class Phase1(Model):
  #...

class Phase2(Model):
  #...

class POObject(Model):
  phase1 = ForeignKey(Phase1, null=True)
  phase2 = ForeignKey(Phase2, null=True, related_name='+')

  def get_phase(self):
    if self.phase2_id: # more likely case first
       return self.phase2
    return self.phase1

  def set_phase(self, phase):
    # Detect which type it is
    if isinstance(phase, Phase2):
      self.phase2 = phase
      self.phase1 = None
    else:
      self.phase2 = None
      self.phase1 = phase

   phase = property(get_phase, set_phase)

在这里,我们有两个可选的FK并使用其中一个。 phase属性隐藏了详细信息,并允许您将两个FK视为一个。将阶段记录分配给po_object.phase时,代码会指出要将FK指向哪个表。