生活过去很简单。我们有一个产品表,每个产品都有产品代码。
不幸的是,我们是一家小型制造商,与几家大型经销商有合作关系,他们非常希望我们在订购时使用他们的产品代码(有时甚至没有礼貌的描述)人类型的查找!)。所以它现在看起来像
Class ProductCode( models.Model)
item_desc = models.ForeignKey( ItemDesc, related_name=product_codes)
code = models.CharField( ...)
customer = ... # FK or Character, unique with code
primary = models.BooleanField( ... ) # wrong way?
...
Class ItemDesc( models.Model)
# code = models.CharField( ...) # what we used to do
primary_code = models.OneToOneField( ProductCode, ... ) # maybe?
...
如何确保每个ItemDesc始终只有一个主要产品代码? (主要是我们在内部用于引用物品的物品,例如库存物品,以及直接销售给最终用户的物品。)
从概念上讲,我使用item_desc使其成为唯一的,但这并不能解决两个相关的问题
它必须成为一个整数,(例如)1 = primary和2..N只存在非唯一性并被视为false。
如何使ItemDesc始终具有主要的ProductCode? p>
或者,从ItemDesc返回到ProductCode的字段primary = OneToOneField(...)
似乎描述了这种关系,但存在循环问题。我必须
使用null item_desc创建产品代码,以便保存它们,所以
它们可以用作1:1的新ItemDesc。然后我必须回去
填写复制1:1关系的产品代码中的item_desc。
我有这种感觉,必须有更好的方法,但我想不到它。或者这可以在ItemDesc模型中隐藏和干燥吗?
答案 0 :(得分:0)
如果我理解你想要的,而不是在ItemDesc级别拥有唯一代码,请参阅客户自己的产品代码。在那种情况下:
1)关于你描述的循环问题:
(i)只需将blank=True, null=True
添加到item_desc
,就可以保存没有null ItemDesc的ProductCode。
(ii)item_desc
本身在您的ProductCode模型中看起来不必要:如果从ItemDesc到ProductCode存在一对一的关系,则反之亦然并自动创建。无需在ProductCode级别重复它(如果您希望此关系是唯一的,则使用ForeignKey更少)。
2)我认为以下内容足以满足您的需求(让您的ItemDesc指向与客户端相关的唯一ProductCode):
Class ProductCode( models.Model):
code = models.CharField( ...)
customer = ... # FK or Character, unique with code
...
Class ItemDesc( models.Model):
primary_code = models.OneToOneField( ProductCode, ... )
==>对于给定的item
,与客户端相关的代码可以item.primary_code.code
访问
==>这不会为每个产品提供唯一的代码,例如,如果两个客户对不同的产品具有相同的参考:您最终会得到2个产品的单个代码。如果您需要,您可能需要通过自动生成内部代码来改进代码,例如在ItemDesc模型中:
@property # So that you can call it simply as if it was an attribute
def internal_code(self):
return str(self.primary_code.customer.id) + '#' + self.primary_code.code
当item.internal_code
返回您客户的产品代码时, item.primary_code.code
会返回您的内部唯一代码。
答案 1 :(得分:0)
好的,回答我自己的问题(虽然我对自己的答案仍然不满意)。这似乎有效:
class ItemDesc( models.Model):
desc = models.CharField( max_length=40, unique=True, blank=False )
primary_code = models.OneToOneField( 'PenBarCode', on_delete=models.SET_NULL, null=True, related_name='_primary')
...
@property
def product_code( self):
if hasattr( self, '_new_primary_code'): return self._new_primary_code
try:
return self.primary_code.itemcode
except ObjectDoesNotExist:
return None
@product_code.setter
def product_code( self, value):
self._new_primary_code = value # lazy. If this object not saved don't want primary code saved
def save( self, *a, **kw):
if self.pk is not None:
# it already exists in the DB, may need also to save changed primary code
if getattr( self, '_new_primary_code', False):
self.primary_code.itemcode = self._new_primary_code
self.primary_code.save()
del self._new_primary_code
return super().save(*a, **kw)
# Adding new object. If we have no product_id then make one up. For demo
if not getattr( self, '_new_primary_code', False):
self._new_primary_code = 'Auto-' + datetime.datetime.now().strftime('%f-%H%M')
# create a code to be primary
p = ProdCode( itemcode=self._new_primary_code)
p.save()
# now it exists we can make it our primary
self.primary_code = p
rc = super().save(*a, **kw)
# and p needs updating to point back on ourself
p.item = self
p.save()
del self._new_primary_code
return rc
from django.core.exceptions import ObjectDoesNotExist
class ProdCode( models.Model):
itemcode = models.CharField( max_length=20, unique=True, blank=True )
item = models.ForeignKey( ItemDesc, null=True, related_name='code_set' )
...
# _primary = OneToOne from ItemDesc, or None if not primary
@property
def primary(self):
try:
return self._primary
except ObjectDoesNotExist:
return None
def save( self, *a, **kw):
if self.primary:
self.item = self.primary
return super().save(*a, **kw)
ItemDesc
与OneToOne
有一个ProdCode
关系。所有ProdCode
个对象都有ForeignKey
到ItemDesc
。因此,可以添加额外的客户特定ProdCode
并使用它们来查找其ItemDesc
。
通过使用属性,可以使主要ProdCode
看起来好像它仍然是ItemDesc
的一部分,因为您可以通过该对象访问和更改它。通过将ProdCode
分配给ProdCode
并保存,我们也可以宣传任何item.primary_code
为on_delete=PROTECT
。
我想将ItemDesc.primary_code
用于OneToOne
,但似乎不太可能让保护和{{1}}一起工作,而不是让对象有效地不可删除创建后的Django。也许我在这里错过了什么?