Django多層ネストManyToManyフィールドORM操作詳細


djangoでプロジェクトを書く時、多くのシーンに出会いました。ORM操作についてデータを取得しましたが、説明しにくいです。Baiduの検索キーワードはどのように検索すればいいのか分かりません。ここで問題を細かくして、シーンを復元して、踏みつけた穴を記録します。
まずモデルを挙げます。生活の中の例を挙げて、問題を理解しやすいです。

#   
class Problem(models.Model):
  desc = models.CharField()
  answer = models.TextField()
  is_pass = models.BooleanField(default=False, verbose_name="    ")

#   
class Chapter(models.Model):
  _id = models.IntegerField(verbose_name="  ")
  title = models.CharField()
  problem = models.ManyToManyField(Problem)
  pass_rate = models.IntegerField(verbose_name="   ")

#     
class Book(models.Model):
  title = models.CharField()
  desc = models.TextField()
  chapter = models.ManyToManyField(Chapter,verbose_name="  ")
  speed = models.IntegerField(verbose_name="    ", default=0)
数学の本だと仮定して、5つの章があります。各章には数の異なる練習問題があります。
つまりbookとchapterは多対多で、chapterとproblemも多対多です。
場面一:書籍の下のすべての練習問題

#                
#            ,     ,              (   )
book.chapter.filter(problem___id__isnull=False).count()
場面二:書籍の下で通過する練習問題
book.chapter.filteris_pass=True).count()
場面三:ある問題がこの本の中にあるかどうかを判断する

  def problem_in_ladder(book, problem):
    for i in book.chapter.all():
      if problem in i.problem.all():
        return True
    return False
できるだけviewでmodelsの値を取る操作を減らすために、上記のいくつかのシーンの方法をmodelsの種類に書いてください。
究極のモデル

#   
class Problem(models.Model):
  desc = models.CharField()
  answer = models.TextField()
  is_pass = models.BooleanField(default=False, verbose_name="    ")

#   
class Chapter(models.Model):
  _id = models.IntegerField(verbose_name="  ")
  title = models.CharField()
  problem = models.ManyToManyField(Problem)
  pass_rate = models.IntegerField(verbose_name="   ")
 
  @property
  def items(self):
    return self.problem.count()

  @property
  def pass_problem(self):
    return self.problem.filter(is_pass=True).count()
  
#     
class Book(models.Model):
  title = models.CharField()
  desc = models.TextField()
  chapter = models.ManyToManyField(Chapter,verbose_name="  ")
  speed = models.IntegerField(verbose_name="    ", default=0)
  
  @property
  def chapters(self):
    return self.chapter.count()

  @property
  def pass_count(self):
    return self.chapter.filter(problem__is_pass=True).count()

  @property
  def items(self):
    return self.chapter.filter(problem___id__isnull=False).count()
知識を補充します:djangoの中でmodelがordengを設置した後に、distinct()とannotate()の問題の記録を使います。
モデルクラスは次の通りです。私はclass Metaにordeng='-date_を設置しました。create''とは、モデルオブジェクトが返した記録結果セットをこのフィールドで並べ替えます。

class SystemUserPushHistory(models.Model):
 
  id = models.UUIDField(default=uuid.uuid4, primary_key=True)
  host_name = models.CharField(max_length=128, null=False)
  system_username = models.CharField(max_length=128, null=False)
  method = models.CharField(max_length=32, null=False)
  is_success = models.BooleanField(default=False)
  date_create = models.DateTimeField(auto_now_add=True, editable=False)
  message = models.CharField(max_length=4096, null=True)
 
  class Meta:
    db_table = "assets_systemuser_push_history"
    ordering = ['-date_create']
 
  def __str__(self):
    ret = self.system_username + " => " + self.host_name
    return ret
業務に需要があるときは、例えばhost_nameはグループ表示を行い、コードにannotateを使用しました。以下の通りです。

>>> from django.db.models import Count 
>>> from assets.models import SystemUserPushHistory
>>> p = SystemUserPushHistory.objects.values("host_name").annotate(dcount=Count(1))
>>> p
<QuerySet [{'host_name': ' 2', 'dcount': 1}, {'host_name': ' 3', 'dcount': 2}, {'host_name': ' 2', 'dcount': 1}, {'host_name': ' 3', 'dcount': 1}]>
>>> print(p.query)
SELECT `assets_systemuser_push_history`.`host_name`, COUNT(1) AS `dcount` FROM `assets_systemuser_push_history` GROUP BY `assets_systemuser_push_history`.`host_name`, `assets_systemuser_push_history`.`date_create` ORDER BY `assets_systemuser_push_history`.`date_create` DESC
得られた結果は私達が予想したようには見えません。その後実行したsqlを出力してグループトップbyにいる時はホストに対して見られます。nameとdate_createでグループ化するのは、modelクラスにordeingを設置しています。削除したらコードが正常に運行されます。
distinctを使うのは上の状況と似ていますので、載せません。
以上のこのDjango多層ネストされたManyToManyフィールドORMの操作の詳細は、小編集が皆さんに提供している内容の全てです。参考にしていただければ幸いです。どうぞよろしくお願いします。