Study/django

django orm deep dive 1 - DJ101 | Django Database ORM Mastery Course

bluebamus 2024. 3. 6.

https://www.youtube.com/watch?v=Iwcl6K2FJds&list=PLOLrQ9Pn6cayYycbeBdxHUFrzTqrNE7Pe&index=1

 

https://github.com/veryacademy/Django-4.x-ORM-Course/

 

GitHub - veryacademy/Django-4.x-ORM-Course

Contribute to veryacademy/Django-4.x-ORM-Course development by creating an account on GitHub.

github.com

 

 - 내가 알고 있는 내용은 넘어가고 필요한 것만 정리한다.

 

 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/

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

https://kimcoder.tistory.com/584

 

[Django] apps.py의 역할

1. apps.py란? - 프로젝트의 전반적인 항목들을 설정하는 곳은 settings.py이고, 각 애플리케이션에 대한 항목들을 설정하는 곳은 apps.py 파일이다. from django.apps import AppConfig class BlogConfig(AppConfig): default

kimcoder.tistory.com

https://codingdog.tistory.com/entry/django-app-config-ready-%ED%95%9C-%EB%B2%88%EB%A7%8C-%EC%8B%A4%ED%96%89%EB%90%98%EA%B2%8C-%ED%95%98%EB%A0%A4%EB%A9%B4-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%A0%EA%B9%8C%EC%9A%94

 

django app config ready 한 번만 실행되게 하려면 어떻게 할까요?

보통 스케쥴러 같은 것을 초기화 할 때 app config 등을 이용하게 되는데요. Appconfig의 ready 메서드를 오버라이드 해서 구현합니다. 사실 공식 문서를 읽어보면, init과 관련된 설명이 많은 것으로 보

codingdog.tistory.com

https://jhoplin7259.tistory.com/206

 

[Django] apps.py의 간단한 활용

apps.py? django application을 생성하면 여러 파일들이 생기는데 그중 apps.py라는 파일이 있다. 이 파일은 프로젝트 디렉토리의 settings.py에 INSTALLED_APPS에 디폴트 app 클래스를 등록하여 프로젝트 애플리

jhoplin7259.tistory.com

 

 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

 

Django Signals 기초

Django 공식 문서를 정리한 글입니다.어떤 동작(action)이 실행되었을 때, 지정된 발신자(senders)에서 수신자(receivers)에게 동작이 실행되었음을 알려주는 기능. 여러 개의 다른 코드가 한 이벤트에 의

velog.io

 

댓글