Django ORM에서 데이터베이스 뷰 사용

때로는 데이터베이스 테이블 대신 데이터베이스 뷰를 사용하는 것이 집계된 데이터를 쿼리하는 데 매우 도움이 될 수 있습니다. 일반적으로 Django ORM에서는 데이터베이스 테이블이 Django ORM 모델에 연결되어 있지만 데이터베이스 뷰를 Django ORM 모델과 연결하는 것도 가능하다는 사실을 발견했습니다. 이렇게 하면 데이터베이스 수준에서 데이터 집계 논리를 숨길 수 있습니다(SQL 생성 보기에서). 그리고 이중 밑줄 외래 키 조회와 같은 대부분의 ORM 기능은 데이터베이스 뷰에 연결된 모델에서 여전히 작동합니다. 이 블로그 게시물에서는 간단한 예를 통해 방법을 설명하겠습니다.

일반 모델은

우리의 예에서는 다음과 같은 일반 모델을 사용합니다. 각 모델에는 연결된 데이터베이스 테이블이 있습니다.

django.db에서 모델 가져오기 클래스 Job(models.Model): 이름 = models.CharField(max_length=200) 클래스 User(models.Model): 이름 = models.CharField(max_length=200) 클래스 Charge(models.Model): HARDWARE = ​​'hw' SOFTWARE = ​​'sw' TYPE_CHOICES = ( (하드웨어, '하드웨어'), (소프트웨어, '소프트웨어'), ) type = models.CharField(choices=TYPE_CHOICES, max_length=2) amount = models.DecimalField( max_digits=8, 소수점=2) 월 = models.DateField() 사용자 = models.ForeignKey(사용자) job = models.ForeignKey(Job, null=True, 공백=True)

Charge 모델은 요금을 저장하는 데 사용됩니다. 사용자 모델에 대한 외래 키와 작업 모델에 대한 선택적 외래 키가 있습니다. 모든 청구 기록을 작업에 연결할 수 있는 것은 아닙니다.

데이터베이스 보기

데이터베이스 뷰는 SQL을 따라 생성되며 원시 SQL 실행 명령을 통해 사용자 정의된 데이터 마이그레이션에 삽입될 수 있습니다.

생성 또는 교체 보기 app_chargesummary AS SELECT row_number() OVER () as id, ci.user_id, ci.job_id, ci.month, SUM(CASE WHEN ci.type = 'hw' THEN ci.amount ELSE 0 END) AS 하드웨어, SUM(CASE WHEN ci.type = 'sw' THEN ci.amount ELSE 0 END) AS 소프트웨어 FROM app_charge ci GROUP BY ci.user_id, ci.job_id, ci.month;

이 보기에는 동일한 작업, 사용자 및 월의 청구 기록에 대한 모든 하드웨어 요금과 소프트웨어 요금이 요약되어 있습니다. 여기에는 다음 열이 포함됩니다.

   [id, user_id, job_id, 월, 하드웨어, 소프트웨어]

다음 단계는 Django ORM을 사용하여 뷰에서 데이터를 검색할 수 있도록 이 뷰에 매핑되는 Django 모델을 만드는 것입니다.

뷰에 대한 모델

데이터베이스 뷰를 매핑하기 위해 아래 모델을 만들었습니다.

클래스 ChargeSummary(models.Model): id = models.BigIntegerField(primary_key=True) 사용자 = models.ForeignKey(User, on_delete=models.DO_NOTHING) job = models.ForeignKey(Job, on_delete=models.DO_NOTHING) 월 = 모델. DateField() 하드웨어 = models.DecimalField(max_digits=19, 소수점_places=2) 소프트웨어 = models.DecimalField(max_digits=19, 소수점_places=2) 클래스 메타: 관리됨 = False db_table = 'app_chargesummary'

위의 모델은 일반 Django 모델과 매우 유사해 보이지만 주목할 만한 세 가지 차이점이 있습니다.

  1. foreignkey 필드의 경우 추가 kwarg를 지정해야 합니다: on_delete=models.DO_NOTHING. 일반적으로 우리는 ChargeSummary 모델에서 delete()를 호출하려고 시도하지 않습니다. 왜냐하면 뒤에 있는 뷰는 쓸 수 없기 때문입니다. 이 kwarg를 지정하지 않으면 일부 테스트 사례가 실패하게 됩니다. 이는 테스트 사례 실행 후 테스트 데이터베이스의 자동 정리 메커니즘과 관련이 있을 수 있습니다.
  2. Managed = False는 South 또는 Django 데이터베이스 자동 마이그레이션이 해당 모델을 무시하고 이에 대한 데이터베이스 스키마 마이그레이션을 생성하려고 시도하지 않도록 Meta 클래스에 지정되어야 합니다.
  3. db_table = 'name_of_the_view'는 Django ORM이 데이터를 검색할 데이터베이스 엔터티를 알 수 있도록 Meta 클래스에 지정되어야 합니다.

새 모델로 쿼리

새 모델이 설정되면 일반 Django 모델과 차이 없이 일반 ORM 쿼리 방법을 사용할 수 있습니다.
모든 ChargeSummary 객체를 가져옵니다.

ChargeSummary.object.all()

2015년 XNUMX월의 모든 ChargeSummary 객체를 가져옵니다.

ChargeSummary.object.filter(월=날짜시간(연도=2015, 월=5, 일=1, tzinfo=utc))

특정 작업 이름을 가진 모든 ChargeSummary 객체를 가져옵니다.

ChargeSummary.object.filter(job__name='테스트 작업')

특정 사용자 이메일 주소가 있는 모든 ChargeSummary 개체를 가져옵니다(사용자 모델과 연결된 1:1 사용자 프로필 모델이 있다고 가정).

ChargeSummary.object.filter(user__profile__email='irwen@rescale.com')

물론 DML SQL은 본질적으로 데이터베이스 뷰에 적용할 수 없기 때문에 save() 또는 delete()와 같은 특정 메소드는 아마도 실패할 것입니다.

아니지만 적어도 마지막으로

이전 버전에서 Django 1.7 이상으로 앱을 업그레이드하는 경우 다음을 실행해야 합니다.

 파이썬 관리.py makemigrations your_app_label

0001_initial.py를 생성한 후 아래 명령을 실행하여 데이터베이스를 업그레이드하세요.

파이썬 관리.py 마이그레이션 --가짜-초기

불행하게도 데이터베이스 보기와 관리되지 않는 ChargeSummary 모델이 있으면 이 작업이 실패합니다. 그 이유는 Django가 가짜 초기 플래그를 사용하여 마이그레이션을 실행할 때 0001_inital.py 마이그레이션의 CreateModel 작업에서 모든 모델에 대한 테이블이 데이터베이스에 존재하는지 확인하기 때문입니다. CreateModel 작업은 모든 모델(managed=False인 모델 포함)에 대해 생성됩니다. 따라서 ChargeSummary 모델의 경우 실제 테이블을 찾을 수 없으므로 실패합니다. 다행히도 0001_initial.py에 create sql 보기를 추가한 다음 가짜 초기 플래그를 사용하여 마이그레이션을 실행하면 이 문제를 해결할 수 있습니다.

... migrations.RunSQL( """ 존재하는 경우 보기 삭제 app_chargesummary; 보기 생성 또는 바꾸기 app_chargesummary ...; """ ), ...

Django ORM으로 즐거운 모델링과 코딩을 즐겨보세요.

비슷한 게시물