StructuredProperty是否引用父或子?

时间:2014-04-24 16:38:07

标签: python google-app-engine google-cloud-datastore

StructuredProperty是否引用父或子?

class Invoices(ndb.Model): #Child

class Customers(ndb.Model): #Parent
    invoice = ndb.StructuredProperty(Invoices)

...或

class Customers(ndb.Model): #Parent

class Invoices(ndb.Model): #Child
    customer = ndb.StructuredProperty(Customers)

1 个答案:

答案 0 :(得分:3)

要在“NoSQL数据存储的更佳实践是什么”的上下文中回答您的问题, 这就是我能提供的。

首先,您可能希望以单数形式命名模型,因为它们应该描述单个模型 InvoiceCustomer实体,而不是几个。

接下来,使用StructuredProperty意味着您希望将所有这些信息保存在 单个实体 - 这将减少写/读操作,但可能会引入一些限制。 (见docs - 或者这个related question

最常见的关系是一个(Customer)到多个(Invoice)关系, 您可以如下构建:

class Invoice(ndb.Model): #Child
    invoice_id = ndb.StringProperty(required=True)  # 

class Customer(ndb.Model): #Parent
    invoices = ndb.StructuredProperty(Invoices, repeated=True)

    def get_invoice_by_id(self, invoice_id):
        """Returns a customer Invoice by invoice_id. Raises KeyError if invoice is not present."""
        invoice_matches = [iv for iv in self.invoices if iv.invoice_id == invoice_id]
        if not invoice_matches: raise KeyError("Customer has no Invoice with ID %s" % invoice_id)
        return invoice_matches[0]  # this could be changed to return all matches

请记住以下对此实施的限制:

  1. StructuredProperty s内部不能包含重复的属性。
  2. 保持invoice_id全局唯一的复杂性将高于Invoice在其自己的实体组中的复杂性。 (invoice_key.get()始终优于此要求的查询))
  3. 您需要Customer上的实例方法才能Invoice找到invoice_id
  4. 您需要逻辑来阻止具有相同ID的发票存在于单个Customer
  5. 以下是一些优点:

    1. 您可以查询Customer
    2. Invoice查询invoice_id将返回Customer个实例以及所有发票。 (这可能是专业人士和骗子,实际上 - 你需要逻辑来从客户那里退回发票)
    3. 这是一个更常见的解决方案,但绝不一定是“正确的解决方案”。 此解决方案使用祖先关系,允许您保持对Invoice和。{ 相关的Customer原子 - 所以你可以维护总计发票统计数据 Customer级别。 (total_orderstotal_gross等。)

      class Invoice(ndb.Model):
          customer = ndb.ComputedProperty(lambda self: self.key.parent(), indexed=False)  # when not indexed, this is essentially a @property
      
      class Customer(ndb.Model):
          def get_invoice_by_id(self, invoice_id):
              """Returns a customer Invoice by invoice_id. Raises KeyError if invoice is not present."""
              invoice_key = ndb.Key(Invoice._get_kind(), invoice_id, parent=self.key)
              return invoice_key.get()
      
          def query_invoices(self):
              """Returns ndb.Query for invoices by this Customer."""
              return self.query(ancestor=self.key)
      
      invoice = Invoice(parent=customer.key, **invoice_properties)
      

      Appengine祝你好运!一旦掌握了所有这些,它就是一个真正有益的平台。

      <强>更新

      如上所述,以下是一些用于事务性更新客户总计的其他代码。

      def create_invoice(customer_key, gross_amount_paid):
          """Creates an invoice for a given customer.
      
          Args:
              customer_key: (ndb.Key) Customer key
              gross_amount_paid: (int) Gross amount paid by customer
          """
      
          @ndb.transactional
          def _txn():
              customer = customer_key.get()
              invoice = Invoice(parent=customer.key, gross_amount=gross_amount_paid)
      
              # Keep an atomic, transactional count of customer aggregates
              customer.total_gross += gross_amount_paid
              customer.total_orders += 1
      
              # batched put for API optimization
              ndb.put_multi([customer, invoice])  
      
              return invoice
      
          return _txn()
      

      上述代码适用于single entity group transaction(例如ndb.transactional(xg=False)),因为InvoiceCustomer的子实体。如果该连接丢失,则需要xg=True。 (我不确定它是否更贵,但它不太优化)