【问题标题】:How to handle making one and only one "primary" product ID per product如何处理为每个产品制作一个且只有一个“主要”产品 ID
【发布时间】:2018-04-05 12:16:06
【问题描述】:

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

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

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 = 主要和 2..N 存在只是为了使非唯一并被视为假。

  2. 如何使 ItemDesc 始终具有主要 ProductCode?​​p>

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

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

  2. 它们可以作为 1:1 与新的 ItemDesc 一起使用。那我就得回去了

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

我有一种感觉,一定有更好的方法,但我想不出。或者这可以在 ItemDesc 模型中隐藏和干燥吗?

【问题讨论】:

  • 这类似于this problem,您的船长是主要代码。这也是某人想做的事情here,答案是重新思考这种关系。我的第一个链接中的几个 cmets 提到在事务结束之前关闭外键检查,但我不确定在创建对象时这在实践中意味着什么。
  • 在更基础的数据库级别上,this question 也很有趣,正如another question 的评论中提到的可延迟外键约束的存在一样。

标签: django database django-models


【解决方案1】:

如果我理解您的需要,请参考您客户自己的产品代码,而不是在 ItemDesc 级别使用唯一代码。在这种情况下:

1)关于您描述的循环问题:

(i) 您可以在没有 null ItemDesc 的情况下保存您的 ProductCode,只需将 blank=True, null=True 添加到 item_desc

(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 访问 ==> 但是,这不会为您提供每个产品的唯一代码,例如,在两个客户对不同产品具有相同参考的情况下:您最终会得到两个产品的单个代码。如果您需要,您可能需要通过自动生成内部代码来改进代码,例如在 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 将返回您客户的产品代码。

【讨论】:

    【解决方案2】:

    好的,回答我自己的问题(尽管我对自己的回答仍然不完全满意)。这似乎有效:

    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 与一个ProdCode 有一个OneToOne 关系。所有ProdCode 对象都有一个ForeignKey 到一个ItemDesc。因此可以添加额外的客户特定ProdCodes 并使用它们来查找他们的ItemDesc

    通过使用属性,可以使主要ProdCode 看起来好像它仍然是ItemDesc 的一部分,因为您可以通过该对象访问和更改它。也可以将任何ProdCode 提升为主要ProdCode,方法是将其分配给item.primary_code 并保存。

    我想将on_delete=PROTECT 用于ItemDesc.primary_code,但要让protect 和OneToOne 一起工作似乎并不容易,除了在创建后通过Django 使对象有效地不可删除。也许我在这里遗漏了什么?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-27
      • 1970-01-01
      • 2017-08-09
      • 2023-04-06
      • 1970-01-01
      • 2012-08-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多