django orm deep dive 1 - DJ101 | Django Database ORM Mastery Course
https://www.youtube.com/watch?v=Iwcl6K2FJds&list=PLOLrQ9Pn6cayYycbeBdxHUFrzTqrNE7Pe&index=1
https://github.com/veryacademy/Django-4.x-ORM-Course/
- 내가 알고 있는 내용은 넘어가고 필요한 것만 정리한다.
1. View Django Raw SQL queries & View all raw SQL queries Django has executed
1) query 속성을 사용해 sql query 출력하기 :
- query는 select문에만 사용이 가능하다.
x = Brand.objects.all().query
print(x)
2) pygments, sqlparse 라이브러리 사용해 sql query 출력하기 :
- 설치 :
pip install pygments sqlparse
- 터미널에서 사용하기 위한 설정 :
- 아래 설정을 shell 모드에 입력한다.
from pygments import highlight
from pygments.formatters import TerminalFormatter
from pygments.lexers import PostgresLexer
from sqlparse import format
- 사용 방법 :
x = Brand.objects.filter(brand_id=1)
sqlformatted = format(str(x.query), reindent=True)
print(hightlight(sqlformatted, PostgresLexer(), TerminalFormatter()))
3) connection, reset_queries로 sql query 출력하기 :
- sql query 확인 :
from django.db import connection
brand.objects.create(brand_id=1, name='nike')
connection.queries
- sql query 초기화 하기 :
from django.db import reset_queries
reset_queries()
connection.queries
[]
2. Custom SQL Inserts Used to Insert New Records in a Table
1) cursor를 이용해 데이터 넣기
- 사용 방법 :
from django.db import connection
from inventory.models import Brand
cursor = connection.cursor()
cursor.execute("INSERT INTO inventory_brand
(brand_id, name, nickname) VALUES (%s, %s, %s)", ['1','nike','nike'])
3. Proxy User Model
1) model manager을 super를 사용하여 구현하는 방법 :
class PersonManagerInactive(models.Manager):
def get_queryset(self):
return super(PersonManagerInactive, self).get_queryset().filter(is_active=False)
class PersonManagerActive(models.Manager):
def get_queryset(self):
return super(PerosnManagerInactive, self).get_queryset().filter(is_active=True)
class Person(User):
inactive = PersonManagerInactive()
active = PersonManagerActive()
class Meta:
proxy = True
ordering = ('first_name',)
@classmethod
def count_all(cls,):
return cls.objects.filter(is_active=True).count()
4. signal로 자동으로 입력을 생성하는 model 정보를 admin에서 inline으로 추가하는 방법
- 이러한 경우 일반적인 방법으로 inline을 정의하고 create를 시도하면 main 데이터가 아직 저장이 되지 않아 pk가 없는 상태이기 때문에, 에러가 발생한다.
- add와 change 오버라이딩 하여 add인 경우에는 inline 추가를 하지 않고, change인 경우 추가를 하도록 만든다.
- can_delete는 다른 page에 추가된 inline 화면에 삭제 기능을 제공할 것인지 여부를 설정한다.
class UserProfileInline(admin.StackedInline):
model = UserProfile
can_delete = False
class AccountsUserAdmin(AuthUserAdmin):
def add_view(self, *args, **kwargs):
self.inlines = []
return super(AccountsUserAdmin, self).add_view(*args, **kwargs)
def change_view(self, *args, **kwargs):
self.inlines = [UserProfileInline]
return super(AccountsUserAdmin, self).change_view(*args, **kwargs)
5. abstract user를 사용하는 경우, UserAdmin 오버라이딩 하는 방법
- 강좌에서는 NewAdmin(AbstractUser)로 새로운 User class를 만들었다. 하지만 admin 페이지 등록은 기존의 UserAdmin을 오버라이딩 하여, NewAdmin에서 만든 필드를 추가하는 방식으로 진행하였다.
- 사실 쉽게 admin.model로 정의하면 이러한 작업을 굳이 할 필요가 없다. 하지만, 방법을 아는건 중요하다.
1) 기존 UserAdmin페이지에 NewUser 필드 추가하기 :
# models.py
from django.contrib.auth.models. import AbstractUser
class NewUser(AbstractUser):
age = models.IntegerField(null=True, blank=True)
nickname = models.CharField(max_length=100, null=True, blank=True)
-------------------------------------------------------------------------
# admin.py
class CustomUseradmin(UserAdmin):
fieldsets = (
*UserAdmin.fieldsets,
(
'Additional Info',
{
'fields':(
'age',
'nickname'
)
}
)
)
admin.site.register(NewUser, CustomUseradmin)
- 결과 :
2) 추가한 NewUser의 필드 위치를 Personal info 항목에 추가하기 :
- 이전 코드를 전부 삭제한다.
- admin.py 파일의 페이지 최상위인 root위치에서 아래 코드를 추가한다.
fields = list(UserAdmin.fieldsets)
fields[1] = ('Personal Info', {'fields':('first_name', 'last_name', 'email', 'age', 'nickname')})
UserAdmin.fieldsets = tuple(fields)
admin.site.register(NewUser, Useradmin)
- 결과 :
6. app.py에 대한 정리
1) apps.py란 :
- 프로젝트의 전반적은 설정을 제어하는 곳은 settings.py이고, 각 어플리케이션에 대한 설정을 제어하는 곳은 apps.py이다.
- 앱의 별칭을 부여하거나 시그널을 등록하는데 주로 사용된다.
from django.apps import AppConfig
class BlogConfig(AppConfig):
defailt_auto_field = 'django.db.models.BigAutoField'
name = 'blog'
2) 앱의 별칭 설정 및 설정 리스트 가져오기
- verbose_name을 apps.py에 설정하고 view에서 가져오기
- apps.get_app_configs()로 가져올 수 있다.
# apps.py
from django.apps import AppConfig
class BlogConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'blog'
verbose_name = 'My Story App'
# views.py
from django.apps import apps
from django.views.generic import TemplateView
class HomeView(TemplateView):
template_name='test.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
dictVerbose={}
for app in apps.get_app_configs(): # settings.py의 INSTALLED_APPS에 등록된 각 앱의 설정 클래스 리스트를 반환
if 'site-packages' not in app.path: # 애플리케이션 디렉토리 경로에 'site-packages'가 없는 경우 (외부 라이브러리 앱이 아닌 경우)
dictVerbose[app.label] = app.verbose_name
context['verbose_dict']=dictVerbose
return context
3) signal의 post_save를 등록하는 방법
- app이 실행되면 무조건 apps.py의 config 클래스에서 ready 함수가 한번 실행된다. 이 함수를 오버라이딩 하여 signal을 등록할 수 있다.
# apps.py
from django.apps import AppConfig
from django.db.models.signals import post_save
def example_reciever(sender, **kwargs):
print("Instance is saved")
class EcommerceConfig(Appconfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'ecommerce'
def ready(self) -> None:
post_save.connect(example_reciever)
# shell
>> Product(name="test").save()
Instance is saved
* settings.py의 INSTALLED_APPS에 설정 클래스 대신 모듈명으로 등록했다면, 앱의 설정 클래스를 참고할 수 없다.
* 이런 경우는 __init__.py의 default_app_config 항목으로 지정된 클래스가 설정 클래스로 사용되며, default_app_config 항목도 정의되지 않으면 장고의 기본 AppConfig 클래스가 설정 클래스로 사용된다.
- reference :
https://docs.djangoproject.com/en/5.0/ref/applications/
https://kimcoder.tistory.com/584
https://jhoplin7259.tistory.com/206
7. Creating a Custom field Subclass
- 직접 field를 만들어 정의할 수 있다.
1) file의 기본속성을 자동으로 정의하는 방법
# models.py
from .fields import ProductIDField
class Product(models.Model):
name = models.CharField(max_length=10)
productid = ProductIDField()
#fields.py
from django.db import models
class ProductIDField(models.CharField):
description = "A unique product identifier"
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 10
kwargs['unique'] = True
super().__init__(*args, **kwargs)
2) pre_save 정의하기
- order 필드에 입력값이 없으면 이전 값 중 가장 마지막에 저장되어 있는 값을 가져와 1을 더하고 이전 데이터가 없으면 1을 저장한다.
# models.py
class Product(models.Model)
name = models.CharField(max_length=10)
productid = ProductIDField()
order = OrderField()
# fields.py
class OrderField(models.PositiveIntegerField):
def pre_save(self, model_instance, add):
if getattr(model_instance, self.attname) is None:
try :
obj = self.model.objects.latest(self.attname)
value = obj.order + 1
except ObjectDoseNotExist:
value = 1
setattr(model_instance, self.attname, value)
return value
else:
return super().pre_save(model_instance, add)
3) db_type, get_prep_value, from_db_values 함수 정의하기
# fields.py
class HexColorField(models.CharField):
description = "A string field for hexadecimal color values"
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 6
super().__init__(*args, **kwargs)
# 데이터베이스에 종속되는 데이터 타입 정의
def db_type(self, connection):
return 'INTEGER(3)'
# 파이썬 객체를 데이터베이스에 저장할 수 있는 타입으로 변환
# 16진수로 해석흐고 10진수 정수로 변환
def get_prep_value(self, value):
if value is None:
return None
return int(value,16)
# 데이터베이스에 저장된 문자열을 파이썬의 정수 리스트로 변환
def from_db_values(self, value, expression, connection):
if value is None:
return None
hex_value = format(value,'X')
return hex_value
4) custom field 함수 정리
from django.db import models
class CommaSeparatedIntegerField(models.Field):
description = "Comma-separated integers"
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 1024 # 예시로 max_length 값을 설정합니다.
super().__init__(*args, **kwargs)
def db_type(self, connection):
# 데이터베이스의 종류에 따라 적절한 데이터 타입을 반환합니다.
# 여기서는 단순화를 위해 문자열 타입을 사용합니다.
return 'text'
def from_db_value(self, value, expression, connection):
if value is None:
return value
# 데이터베이스에 저장된 문자열을 파이썬의 정수 리스트로 변환합니다.
return [int(item) for item in value.split(',')]
def to_python(self, value):
if isinstance(value, list):
return value
# 문자열 값을 파이썬 객체로 변환합니다.
if value is None:
return []
return [int(item) for item in value.split(',')]
def get_prep_value(self, value):
# 파이썬 객체를 데이터베이스에 저장할 수 있는 형태로 변환합니다.
if value is None:
return ''
# 리스트의 정수들을 문자열로 결합합니다.
return ','.join(str(i) for i in value)
def value_to_string(self, obj):
# Django의 serialization framework를 위한 문자열 변환 메서드입니다.
value = self.value_from_object(obj)
return self.get_prep_value(value)
# models.py 안에 모델에 필드를 추가합니다.
class MyModel(models.Model):
numbers = CommaSeparatedIntegerField()
- reference :
https://velog.io/@thebjko/Django-Signals-Basic
'Study > django' 카테고리의 다른 글
[DRF] drf-writable-nested 사용 방법에 대한 정리 (0) | 2024.03.07 |
---|---|
django orm deep dive 2 - Django 5 ORM Model Essentials 2024 (1) | 2024.03.07 |
[UDEMY]Django DRF Project: eCommerce RESTful API 강의 평가 (0) | 2024.03.01 |
[UDEMY]Django DRF Project: eCommerce RESTful API 강의 정리 (0) | 2024.03.01 |
Django ORM (QuerySet)구조와 원리 그리고 최적화전략 - 정리 (0) | 2024.02.22 |
댓글