如何处理制作一个且只有一个" primary"每件商品的产品ID

时间:2018-04-05 12:16:06

标签: django database django-models

生活过去很简单。我们有一个产品表,每个产品都有产品代码。

不幸的是,我们是一家小型制造商,与几家大型经销商有合作关系,他们非常希望我们在订购时使用他们的产品代码(有时甚至没有礼貌的描述)人类型的查找!)。所以它现在看起来像

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. 它必须成为一个整数,(例如)1 = primary和2..N只存在非唯一性并被视为false。

  2. 如何使ItemDesc始终具有主要的ProductCode?

  3. 或者,从ItemDesc返回到ProductCode的字段primary = OneToOneField(...)似乎描述了这种关系,但存在循环问题。我必须

    1. 使用null item_desc创建产品代码,以便保存它们,所以

    2. 它们可以用作1:1的新ItemDesc。然后我必须回去

    3. 填写复制1:1关系的产品代码中的item_desc。

    4. 我有这种感觉,必须有更好的方法,但我想不到它。或者这可以在ItemDesc模型中隐藏和干燥吗?

2 个答案:

答案 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)

ItemDescOneToOne有一个ProdCode关系。所有ProdCode个对象都有ForeignKeyItemDesc。因此,可以添加额外的客户特定ProdCode并使用它们来查找其ItemDesc

通过使用属性,可以使主要ProdCode看起来好像它仍然是ItemDesc的一部分,因为您可以通过该对象访问和更改它。通过将ProdCode分配给ProdCode并保存,我们也可以宣传任何item.primary_codeon_delete=PROTECT

我想将ItemDesc.primary_code用于OneToOne,但似乎不太可能让保护和{{1}}一起工作,而不是让对象有效地不可删除创建后的Django。也许我在这里错过了什么?